Quellcode durchsuchen

Merge tag 'media/v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull media updates from Mauro Carvalho Chehab:

 - new CEC pin injection code for testing purposes

 - DVB frontend cxd2099 promoted from staging

 - new platform driver for Sony cxd2880 DVB devices

 - new sensor drivers: mt9t112, ov2685, ov5695, ov772x, tda1997x,
   tw9910.c

 - removal of unused cx18 and ivtv alsa mixers

 - the reneseas-ceu driver doesn't depend on soc_camera anymore and
   moved from staging

 - removed the mantis_vp3028 driver, unused since 2009

 - s5p-mfc: add support for version 10 of the MSP

 - added a decoder for imon protocol

 - atomisp: lots of cleanups

 - imx074 and mt9t031: don't depend on soc_camera anymore, being
   promoted from staging

 - added helper functions to better support DVB I2C binding

 - lots of driver improvements and cleanups

* tag 'media/v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (438 commits)
  media: v4l2-ioctl: rename a temp var that stores _IOC_SIZE(cmd)
  media: fimc-capture: get rid of two warnings
  media: dvb-usb-v2: fix a missing dependency of I2C_MUX
  media: uvc: to the right check at uvc_ioctl_enum_framesizes()
  media: cec-core: fix a bug at cec_error_inj_write()
  media: tda9840: cleanup a warning
  media: tm6000:  avoid casting just to print pointer address
  media: em28xx-input: improve error handling code
  media: zr364xx: avoid casting just to print pointer address
  media: vivid-radio-rx: add a cast to avoid a warning
  media: saa7134-alsa: don't use casts to print a buffer address
  media: solo6x10: get rid of an address space warning
  media: zoran: don't cast pointers to print them
  media: ir-kbd-i2c: change the if logic to avoid a warning
  media: ir-kbd-i2c: improve error handling code
  media: saa7134-input: improve error handling
  media: s2255drv: fix a casting warning
  media: ivtvfb: Cleanup some warnings
  media: videobuf-dma-sg: Fix a weird cast
  soc_camera: fix a weird cast on printk
  ...
Linus Torvalds vor 7 Jahren
Ursprung
Commit
ef1c4a6fa9
100 geänderte Dateien mit 16828 neuen und 2017 gelöschten Zeilen
  1. 40 0
      Documentation/ABI/testing/debugfs-cec-error-inj
  2. 3 2
      Documentation/devicetree/bindings/media/coda.txt
  3. 16 2
      Documentation/devicetree/bindings/media/i2c/adv7604.txt
  4. 41 0
      Documentation/devicetree/bindings/media/i2c/ov2685.txt
  5. 41 0
      Documentation/devicetree/bindings/media/i2c/ov5695.txt
  6. 14 2
      Documentation/devicetree/bindings/media/i2c/ov7670.txt
  7. 36 0
      Documentation/devicetree/bindings/media/i2c/ov9650.txt
  8. 178 0
      Documentation/devicetree/bindings/media/i2c/tda1997x.txt
  9. 2 2
      Documentation/devicetree/bindings/media/rcar_vin.txt
  10. 81 0
      Documentation/devicetree/bindings/media/renesas,ceu.txt
  11. 1 0
      Documentation/devicetree/bindings/media/s5p-mfc.txt
  12. 14 0
      Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
  13. 3 0
      Documentation/devicetree/bindings/media/sunxi-ir.txt
  14. 71 1
      Documentation/media/kapi/cec-core.rst
  15. 1 0
      Documentation/media/lirc.h.rst.exceptions
  16. 1 0
      Documentation/media/uapi/cec/cec-api.rst
  17. 325 0
      Documentation/media/uapi/cec/cec-pin-error-inj.rst
  18. 15 4
      Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst
  19. 18 0
      Documentation/media/uapi/mediactl/media-ioc-enum-links.rst
  20. 43 11
      Documentation/media/uapi/mediactl/media-ioc-g-topology.rst
  21. 17 6
      Documentation/media/uapi/mediactl/media-types.rst
  22. 0 1
      Documentation/media/uapi/rc/lirc-dev-intro.rst
  23. 1 1
      Documentation/media/uapi/v4l/buffer.rst
  24. 410 0
      Documentation/media/uapi/v4l/extended-controls.rst
  25. 4 4
      Documentation/media/uapi/v4l/func-poll.rst
  26. 5 0
      Documentation/media/uapi/v4l/pixfmt-compressed.rst
  27. 1 1
      Documentation/media/uapi/v4l/pixfmt-v4l2.rst
  28. 6 2
      Documentation/media/uapi/v4l/subdev-formats.rst
  29. 4 3
      Documentation/media/uapi/v4l/vidioc-g-parm.rst
  30. 1 1
      Documentation/media/uapi/v4l/vidioc-prepare-buf.rst
  31. 14 12
      Documentation/media/v4l-drivers/imx.rst
  32. 90 2
      MAINTAINERS
  33. 169 169
      arch/sh/boards/mach-ecovec24/setup.c
  34. 100 125
      arch/sh/boards/mach-migor/setup.c
  35. 1 1
      arch/sh/kernel/cpu/sh4a/clock-sh7722.c
  36. 2 2
      arch/sh/kernel/cpu/sh4a/clock-sh7724.c
  37. 177 1
      drivers/input/touchscreen/sur40.c
  38. 1 0
      drivers/media/Kconfig
  39. 6 0
      drivers/media/cec/Kconfig
  40. 4 0
      drivers/media/cec/Makefile
  41. 23 31
      drivers/media/cec/cec-adap.c
  42. 1 13
      drivers/media/cec/cec-api.c
  43. 59 13
      drivers/media/cec/cec-core.c
  44. 1 13
      drivers/media/cec/cec-edid.c
  45. 1 13
      drivers/media/cec/cec-notifier.c
  46. 342 0
      drivers/media/cec/cec-pin-error-inj.c
  47. 128 20
      drivers/media/cec/cec-pin-priv.h
  48. 591 87
      drivers/media/cec/cec-pin.c
  49. 1 13
      drivers/media/cec/cec-priv.h
  50. 22 11
      drivers/media/common/siano/smscoreapi.c
  51. 2 0
      drivers/media/common/siano/smscoreapi.h
  52. 1 13
      drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c
  53. 3 15
      drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
  54. 9 0
      drivers/media/common/videobuf2/videobuf2-core.c
  55. 1 1
      drivers/media/common/videobuf2/videobuf2-vmalloc.c
  56. 2 2
      drivers/media/dvb-core/dvb_ca_en50221.c
  57. 2 2
      drivers/media/dvb-core/dvb_frontend.c
  58. 50 0
      drivers/media/dvb-core/dvbdev.c
  59. 24 8
      drivers/media/dvb-frontends/Kconfig
  60. 2 0
      drivers/media/dvb-frontends/Makefile
  61. 465 444
      drivers/media/dvb-frontends/af9013.c
  62. 13 35
      drivers/media/dvb-frontends/af9013.h
  63. 756 802
      drivers/media/dvb-frontends/af9013_priv.h
  64. 104 105
      drivers/media/dvb-frontends/cxd2099.c
  65. 3 16
      drivers/media/dvb-frontends/cxd2099.h
  66. 8 0
      drivers/media/dvb-frontends/cxd2880/Kconfig
  67. 18 0
      drivers/media/dvb-frontends/cxd2880/Makefile
  68. 29 0
      drivers/media/dvb-frontends/cxd2880/cxd2880.h
  69. 21 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
  70. 19 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
  71. 129 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
  72. 23 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
  73. 29 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
  74. 74 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
  75. 385 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
  76. 72 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
  77. 27 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
  78. 66 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
  79. 54 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
  80. 34 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
  81. 113 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
  82. 26 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
  83. 3519 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
  84. 365 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
  85. 12 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
  86. 919 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
  87. 45 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
  88. 1217 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
  89. 65 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
  90. 1878 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
  91. 135 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
  92. 775 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
  93. 77 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
  94. 150 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
  95. 29 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
  96. 1947 0
      drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
  97. 2 2
      drivers/media/dvb-frontends/dib0090.c
  98. 1 1
      drivers/media/dvb-frontends/dib7000p.c
  99. 1 1
      drivers/media/dvb-frontends/dib8000.c
  100. 1 1
      drivers/media/dvb-frontends/dibx000_common.c

+ 40 - 0
Documentation/ABI/testing/debugfs-cec-error-inj

@@ -0,0 +1,40 @@
+What:		/sys/kernel/debug/cec/*/error-inj
+Date:		March 2018
+Contact:	Hans Verkuil <hans.verkuil@cisco.com>
+Description:
+
+The CEC Framework allows for CEC error injection commands through
+debugfs. Drivers that support this will create an error-inj file
+through which the error injection commands can be given.
+
+The basic syntax is as follows:
+
+Leading spaces/tabs are ignored. If the next character is a '#' or the
+end of the line was reached, then the whole line is ignored. Otherwise
+a command is expected.
+
+It is up to the driver to decide what commands to implement. The only
+exception is that the command 'clear' without any arguments must be
+implemented and that it will remove all current error injection
+commands.
+
+This ensures that you can always do 'echo clear >error-inj' to clear any
+error injections without having to know the details of the driver-specific
+commands.
+
+Note that the output of 'error-inj' shall be valid as input to 'error-inj'.
+So this must work:
+
+	$ cat error-inj >einj.txt
+	$ cat einj.txt >error-inj
+
+Other than these basic rules described above this ABI is not considered
+stable and may change in the future.
+
+Drivers that implement this functionality must document the commands as
+part of the CEC documentation and must keep that documentation up to date
+when changes are made.
+
+The following CEC error injection implementations exist:
+
+- Documentation/media/uapi/cec/cec-pin-error-inj.rst

+ 3 - 2
Documentation/devicetree/bindings/media/coda.txt

@@ -7,8 +7,9 @@ called VPU (Video Processing Unit).
 Required properties:
 - compatible : should be "fsl,<chip>-src" for i.MX SoCs:
   (a) "fsl,imx27-vpu" for CodaDx6 present in i.MX27
-  (b) "fsl,imx53-vpu" for CODA7541 present in i.MX53
-  (c) "fsl,imx6q-vpu" for CODA960 present in i.MX6q
+  (b) "fsl,imx51-vpu" for CodaHx4 present in i.MX51
+  (c) "fsl,imx53-vpu" for CODA7541 present in i.MX53
+  (d) "fsl,imx6q-vpu" for CODA960 present in i.MX6q
 - reg: should be register base and length as documented in the
   SoC reference manual
 - interrupts : Should contain the VPU interrupt. For CODA960,

+ 16 - 2
Documentation/devicetree/bindings/media/i2c/adv7604.txt

@@ -13,7 +13,11 @@ Required Properties:
     - "adi,adv7611" for the ADV7611
     - "adi,adv7612" for the ADV7612
 
-  - reg: I2C slave address
+  - reg: I2C slave addresses
+    The ADV76xx has up to thirteen 256-byte maps that can be accessed via the
+    main I2C ports. Each map has it own I2C address and acts as a standard
+    slave device on the I2C bus. The main address is mandatory, others are
+    optional and revert to defaults if not specified.
 
   - hpd-gpios: References to the GPIOs that control the HDMI hot-plug
     detection pins, one per HDMI input. The active flag indicates the GPIO
@@ -35,6 +39,11 @@ Optional Properties:
 
   - reset-gpios: Reference to the GPIO connected to the device's reset pin.
   - default-input: Select which input is selected after reset.
+  - reg-names : Names of maps with programmable addresses.
+		It can contain any map needing a non-default address.
+		Possible maps names are :
+		  "main", "avlink", "cec", "infoframe", "esdp", "dpp", "afe",
+		  "rep", "edid", "hdmi", "test", "cp", "vdp"
 
 Optional Endpoint Properties:
 
@@ -52,7 +61,12 @@ Example:
 
 	hdmi_receiver@4c {
 		compatible = "adi,adv7611";
-		reg = <0x4c>;
+		/*
+		 * The edid page will be accessible @ 0x66 on the I2C bus. All
+		 * other maps will retain their default addresses.
+		 */
+		reg = <0x4c>, <0x66>;
+		reg-names "main", "edid";
 
 		reset-gpios = <&ioexp 0 GPIO_ACTIVE_LOW>;
 		hpd-gpios = <&ioexp 2 GPIO_ACTIVE_HIGH>;

+ 41 - 0
Documentation/devicetree/bindings/media/i2c/ov2685.txt

@@ -0,0 +1,41 @@
+* Omnivision OV2685 MIPI CSI-2 sensor
+
+Required Properties:
+- compatible: shall be "ovti,ov2685"
+- clocks: reference to the xvclk input clock
+- clock-names: shall be "xvclk"
+- avdd-supply: Analog voltage supply, 2.8 volts
+- dovdd-supply: Digital I/O voltage supply, 1.8 volts
+- dvdd-supply: Digital core voltage supply, 1.8 volts
+- reset-gpios: Low active reset gpio
+
+The device node shall contain one 'port' child node with an
+'endpoint' subnode for its digital output video port,
+in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+The endpoint optional property 'data-lanes' shall be "<1>".
+
+Example:
+&i2c7 {
+	ov2685: camera-sensor@3c {
+		compatible = "ovti,ov2685";
+		reg = <0x3c>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&clk_24m_cam>;
+
+		clocks = <&cru SCLK_TESTCLKOUT1>;
+		clock-names = "xvclk";
+
+		avdd-supply = <&pp2800_cam>;
+		dovdd-supply = <&pp1800>;
+		dvdd-supply = <&pp1800>;
+		reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
+
+		port {
+			ucam_out: endpoint {
+				remote-endpoint = <&mipi_in_ucam>;
+				data-lanes = <1>;
+			};
+		};
+	};
+};

+ 41 - 0
Documentation/devicetree/bindings/media/i2c/ov5695.txt

@@ -0,0 +1,41 @@
+* Omnivision OV5695 MIPI CSI-2 sensor
+
+Required Properties:
+- compatible: shall be "ovti,ov5695"
+- clocks: reference to the xvclk input clock
+- clock-names: shall be "xvclk"
+- avdd-supply: Analog voltage supply, 2.8 volts
+- dovdd-supply: Digital I/O voltage supply, 1.8 volts
+- dvdd-supply: Digital core voltage supply, 1.2 volts
+- reset-gpios: Low active reset gpio
+
+The device node shall contain one 'port' child node with an
+'endpoint' subnode for its digital output video port,
+in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+The endpoint optional property 'data-lanes' shall be "<1 2>".
+
+Example:
+&i2c7 {
+	ov5695: camera-sensor@36 {
+		compatible = "ovti,ov5695";
+		reg = <0x36>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&clk_24m_cam>;
+
+		clocks = <&cru SCLK_TESTCLKOUT1>;
+		clock-names = "xvclk";
+
+		avdd-supply = <&pp2800_cam>;
+		dovdd-supply = <&pp1800>;
+		dvdd-supply = <&pp1250_cam>;
+		reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
+
+		port {
+			wcam_out: endpoint {
+				remote-endpoint = <&mipi_in_wcam>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
+};

+ 14 - 2
Documentation/devicetree/bindings/media/i2c/ov7670.txt

@@ -9,14 +9,21 @@ Required Properties:
 - clocks: reference to the xclk input clock.
 - clock-names: should be "xclk".
 
+Required Endpoint Properties:
+- hsync-active: active state of the HSYNC signal, 0/1 for LOW/HIGH respectively.
+- vsync-active: active state of the VSYNC signal, 0/1 for LOW/HIGH respectively.
+
 Optional Properties:
 - reset-gpios: reference to the GPIO connected to the resetb pin, if any.
   Active is low.
 - powerdown-gpios: reference to the GPIO connected to the pwdn pin, if any.
   Active is high.
+- ov7670,pclk-hb-disable: a boolean property to suppress pixel clock output
+  signal during horizontal blankings.
 
-The device node must contain one 'port' child node for its digital output
-video port, in accordance with the video interface bindings defined in
+The device node must contain one 'port' child node with one 'endpoint' child
+sub-node for its digital output video port, in accordance with the video
+interface bindings defined in:
 Documentation/devicetree/bindings/media/video-interfaces.txt.
 
 Example:
@@ -34,8 +41,13 @@ Example:
 			assigned-clocks = <&pck0>;
 			assigned-clock-rates = <25000000>;
 
+			ov7670,pclk-hb-disable;
+
 			port {
 				ov7670_0: endpoint {
+					hsync-active = <0>;
+					vsync-active = <0>;
+
 					remote-endpoint = <&isi_0>;
 				};
 			};

+ 36 - 0
Documentation/devicetree/bindings/media/i2c/ov9650.txt

@@ -0,0 +1,36 @@
+* Omnivision OV9650/OV9652 CMOS sensor
+
+Required Properties:
+- compatible: shall be one of
+	"ovti,ov9650"
+	"ovti,ov9652"
+- clocks: reference to the xvclk input clock.
+
+Optional Properties:
+- reset-gpios: reference to the GPIO connected to the resetb pin, if any.
+  Active is high.
+- powerdown-gpios: reference to the GPIO connected to the pwdn pin, if any.
+  Active is high.
+
+The device node shall contain one 'port' child node with one child 'endpoint'
+subnode for its digital output video port, in accordance with the video
+interface bindings defined in Documentation/devicetree/bindings/media/
+video-interfaces.txt.
+
+Example:
+
+&i2c0 {
+	ov9650: camera@30 {
+		compatible = "ovti,ov9650";
+		reg = <0x30>;
+		reset-gpios = <&axi_gpio_0 0 GPIO_ACTIVE_HIGH>;
+		powerdown-gpios = <&axi_gpio_0 1 GPIO_ACTIVE_HIGH>;
+		clocks = <&xclk>;
+
+		port {
+			ov9650_0: endpoint {
+				remote-endpoint = <&vcap1_in0>;
+			};
+		};
+	};
+};

+ 178 - 0
Documentation/devicetree/bindings/media/i2c/tda1997x.txt

@@ -0,0 +1,178 @@
+Device-Tree bindings for the NXP TDA1997x HDMI receiver
+
+The TDA19971/73 are HDMI video receivers.
+
+The TDA19971 Video port output pins can be used as follows:
+ - RGB 8bit per color (24 bits total): R[11:4] B[11:4] G[11:4]
+ - YUV444 8bit per color (24 bits total): Y[11:4] Cr[11:4] Cb[11:4]
+ - YUV422 semi-planar 8bit per component (16 bits total): Y[11:4] CbCr[11:4]
+ - YUV422 semi-planar 10bit per component (20 bits total): Y[11:2] CbCr[11:2]
+ - YUV422 semi-planar 12bit per component (24 bits total): - Y[11:0] CbCr[11:0]
+ - YUV422 BT656 8bit per component (8 bits total): YCbCr[11:4] (2-cycles)
+ - YUV422 BT656 10bit per component (10 bits total): YCbCr[11:2] (2-cycles)
+ - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles)
+
+The TDA19973 Video port output pins can be used as follows:
+ - RGB 12bit per color (36 bits total): R[11:0] B[11:0] G[11:0]
+ - YUV444 12bit per color (36 bits total): Y[11:0] Cb[11:0] Cr[11:0]
+ - YUV422 semi-planar 12bit per component (24 bits total): Y[11:0] CbCr[11:0]
+ - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles)
+
+The Video port output pins are mapped via 4-bit 'pin groups' allowing
+for a variety of connection possibilities including swapping pin order within
+pin groups. The video_portcfg device-tree property consists of register mapping
+pairs which map a chip-specific VP output register to a 4-bit pin group. If
+the pin group needs to be bit-swapped you can use the *_S pin-group defines.
+
+Required Properties:
+ - compatible          :
+  - "nxp,tda19971" for the TDA19971
+  - "nxp,tda19973" for the TDA19973
+ - reg                 : I2C slave address
+ - interrupts          : The interrupt number
+ - DOVDD-supply        : Digital I/O supply
+ - DVDD-supply         : Digital Core supply
+ - AVDD-supply         : Analog supply
+ - nxp,vidout-portcfg  : array of pairs mapping VP output pins to pin groups.
+
+Optional Properties:
+ - nxp,audout-format   : DAI bus format: "i2s" or "spdif".
+ - nxp,audout-width    : width of audio output data bus (1-4).
+ - nxp,audout-layout   : data layout (0=AP0 used, 1=AP0/AP1/AP2/AP3 used).
+ - nxp,audout-mclk-fs  : Multiplication factor between stream rate and codec
+                         mclk.
+
+The port node shall contain one endpoint child node for its digital
+output video port, in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Optional Endpoint Properties:
+  The following three properties are defined in video-interfaces.txt and
+  are valid for the output parallel bus endpoint:
+  - hsync-active: Horizontal synchronization polarity. Defaults to active high.
+  - vsync-active: Vertical synchronization polarity. Defaults to active high.
+  - data-active: Data polarity. Defaults to active high.
+
+Examples:
+ - VP[15:0] connected to IMX6 CSI_DATA[19:4] for 16bit YUV422
+   16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins)
+	hdmi-receiver@48 {
+		compatible = "nxp,tda19971";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_tda1997x>;
+		reg = <0x48>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
+		DOVDD-supply = <&reg_3p3v>;
+		AVDD-supply = <&reg_1p8v>;
+		DVDD-supply = <&reg_1p8v>;
+		/* audio */
+		#sound-dai-cells = <0>;
+		nxp,audout-format = "i2s";
+		nxp,audout-layout = <0>;
+		nxp,audout-width = <16>;
+		nxp,audout-mclk-fs = <128>;
+		/*
+		 * The 8bpp YUV422 semi-planar mode outputs CbCr[11:4]
+		 * and Y[11:4] across 16bits in the same pixclk cycle.
+		 */
+		nxp,vidout-portcfg =
+			/* Y[11:8]<->VP[15:12]<->CSI_DATA[19:16] */
+			< TDA1997X_VP24_V15_12 TDA1997X_G_Y_11_8 >,
+			/* Y[7:4]<->VP[11:08]<->CSI_DATA[15:12] */
+			< TDA1997X_VP24_V11_08 TDA1997X_G_Y_7_4 >,
+			/* CbCc[11:8]<->VP[07:04]<->CSI_DATA[11:8] */
+			< TDA1997X_VP24_V07_04 TDA1997X_R_CR_CBCR_11_8 >,
+			/* CbCr[7:4]<->VP[03:00]<->CSI_DATA[7:4] */
+			< TDA1997X_VP24_V03_00 TDA1997X_R_CR_CBCR_7_4 >;
+
+		port {
+			tda1997x_to_ipu1_csi0_mux: endpoint {
+				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+				bus-width = <16>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+				data-active = <1>;
+			};
+		};
+	};
+ - VP[15:8] connected to IMX6 CSI_DATA[19:12] for 8bit BT656
+   16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins)
+	hdmi-receiver@48 {
+		compatible = "nxp,tda19971";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_tda1997x>;
+		reg = <0x48>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
+		DOVDD-supply = <&reg_3p3v>;
+		AVDD-supply = <&reg_1p8v>;
+		DVDD-supply = <&reg_1p8v>;
+		/* audio */
+		#sound-dai-cells = <0>;
+		nxp,audout-format = "i2s";
+		nxp,audout-layout = <0>;
+		nxp,audout-width = <16>;
+		nxp,audout-mclk-fs = <128>;
+		/*
+		 * The 8bpp YUV422 semi-planar mode outputs CbCr[11:4]
+		 * and Y[11:4] across 16bits in the same pixclk cycle.
+		 */
+		nxp,vidout-portcfg =
+			/* Y[11:8]<->VP[15:12]<->CSI_DATA[19:16] */
+			< TDA1997X_VP24_V15_12 TDA1997X_G_Y_11_8 >,
+			/* Y[7:4]<->VP[11:08]<->CSI_DATA[15:12] */
+			< TDA1997X_VP24_V11_08 TDA1997X_G_Y_7_4 >,
+			/* CbCc[11:8]<->VP[07:04]<->CSI_DATA[11:8] */
+			< TDA1997X_VP24_V07_04 TDA1997X_R_CR_CBCR_11_8 >,
+			/* CbCr[7:4]<->VP[03:00]<->CSI_DATA[7:4] */
+			< TDA1997X_VP24_V03_00 TDA1997X_R_CR_CBCR_7_4 >;
+
+		port {
+			tda1997x_to_ipu1_csi0_mux: endpoint {
+				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+				bus-width = <16>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+				data-active = <1>;
+			};
+		};
+	};
+ - VP[15:8] connected to IMX6 CSI_DATA[19:12] for 8bit BT656
+   16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins)
+	hdmi-receiver@48 {
+		compatible = "nxp,tda19971";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_tda1997x>;
+		reg = <0x48>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
+		DOVDD-supply = <&reg_3p3v>;
+		AVDD-supply = <&reg_1p8v>;
+		DVDD-supply = <&reg_1p8v>;
+		/* audio */
+		#sound-dai-cells = <0>;
+		nxp,audout-format = "i2s";
+		nxp,audout-layout = <0>;
+		nxp,audout-width = <16>;
+		nxp,audout-mclk-fs = <128>;
+		/*
+		 * The 8bpp BT656 mode outputs YCbCr[11:4] across 8bits over
+		 * 2 pixclk cycles.
+		 */
+		nxp,vidout-portcfg =
+			/* YCbCr[11:8]<->VP[15:12]<->CSI_DATA[19:16] */
+			< TDA1997X_VP24_V15_12 TDA1997X_R_CR_CBCR_11_8 >,
+			/* YCbCr[7:4]<->VP[11:08]<->CSI_DATA[15:12] */
+			< TDA1997X_VP24_V11_08 TDA1997X_R_CR_CBCR_7_4 >,
+
+		port {
+			tda1997x_to_ipu1_csi0_mux: endpoint {
+				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+				bus-width = <16>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+				data-active = <1>;
+			};
+		};
+	};

+ 2 - 2
Documentation/devicetree/bindings/media/rcar_vin.txt

@@ -56,7 +56,7 @@ Board setup example (vin1 composite video input)
 ------------------------------------------------
 
 &i2c2   {
-        status = "ok";
+        status = "okay";
         pinctrl-0 = <&i2c2_pins>;
         pinctrl-names = "default";
 
@@ -79,7 +79,7 @@ Board setup example (vin1 composite video input)
         pinctrl-0 = <&vin1_pins>;
         pinctrl-names = "default";
 
-        status = "ok";
+        status = "okay";
 
         port {
                 #address-cells = <1>;

+ 81 - 0
Documentation/devicetree/bindings/media/renesas,ceu.txt

@@ -0,0 +1,81 @@
+Renesas Capture Engine Unit (CEU)
+----------------------------------------------
+
+The Capture Engine Unit is the image capture interface found in the Renesas
+SH Mobile and RZ SoCs.
+
+The interface supports a single parallel input with data bus width of 8 or 16
+bits.
+
+Required properties:
+- compatible: Shall be "renesas,r7s72100-ceu" for CEU units found in RZ/A1H
+  and RZ/A1M SoCs.
+- reg: Registers address base and size.
+- interrupts: The interrupt specifier.
+
+The CEU supports a single parallel input and should contain a single 'port'
+subnode with a single 'endpoint'. Connection to input devices are modeled
+according to the video interfaces OF bindings specified in:
+Documentation/devicetree/bindings/media/video-interfaces.txt
+
+Optional endpoint properties applicable to parallel input bus described in
+the above mentioned "video-interfaces.txt" file are supported.
+
+- hsync-active: Active state of the HSYNC signal, 0/1 for LOW/HIGH respectively.
+  If property is not present, default is active high.
+- vsync-active: Active state of the VSYNC signal, 0/1 for LOW/HIGH respectively.
+  If property is not present, default is active high.
+
+Example:
+
+The example describes the connection between the Capture Engine Unit and an
+OV7670 image sensor connected to i2c1 interface.
+
+ceu: ceu@e8210000 {
+	reg = <0xe8210000 0x209c>;
+	compatible = "renesas,r7s72100-ceu";
+	interrupts = <GIC_SPI 332 IRQ_TYPE_LEVEL_HIGH>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&vio_pins>;
+
+	status = "okay";
+
+	port {
+		ceu_in: endpoint {
+			remote-endpoint = <&ov7670_out>;
+
+			hsync-active = <1>;
+			vsync-active = <0>;
+		};
+	};
+};
+
+i2c1: i2c@fcfee400 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+
+	status = "okay";
+
+	clock-frequency = <100000>;
+
+	ov7670: camera@21 {
+		compatible = "ovti,ov7670";
+		reg = <0x21>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&vio_pins>;
+
+		reset-gpios = <&port3 11 GPIO_ACTIVE_LOW>;
+		powerdown-gpios = <&port3 12 GPIO_ACTIVE_HIGH>;
+
+		port {
+			ov7670_out: endpoint {
+				remote-endpoint = <&ceu_in>;
+
+				hsync-active = <1>;
+				vsync-active = <0>;
+			};
+		};
+	};
+};

+ 1 - 0
Documentation/devicetree/bindings/media/s5p-mfc.txt

@@ -13,6 +13,7 @@ Required properties:
 	(c) "samsung,mfc-v7" for MFC v7 present in Exynos5420 SoC
 	(d) "samsung,mfc-v8" for MFC v8 present in Exynos5800 SoC
 	(e) "samsung,exynos5433-mfc" for MFC v8 present in Exynos5433 SoC
+	(f) "samsung,mfc-v10" for MFC v10 present in Exynos7880 SoC
 
   - reg : Physical base address of the IP registers and length of memory
 	  mapped region.

+ 14 - 0
Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt

@@ -0,0 +1,14 @@
+Sony CXD2880 DVB-T2/T tuner + demodulator driver SPI adapter
+
+Required properties:
+- compatible: Should be "sony,cxd2880".
+- reg: SPI chip select number for the device.
+- spi-max-frequency: Maximum bus speed, should be set to <55000000> (55MHz).
+
+Example:
+
+cxd2880@0 {
+	compatible = "sony,cxd2880";
+	reg = <0>; /* CE0 */
+	spi-max-frequency = <55000000>; /* 55MHz */
+};

+ 3 - 0
Documentation/devicetree/bindings/media/sunxi-ir.txt

@@ -11,6 +11,8 @@ Required properties:
 Optional properties:
 - linux,rc-map-name: see rc.txt file in the same directory.
 - resets : phandle + reset specifier pair
+- clock-frequency  : IR Receiver clock frequency, in Hertz. Defaults to 8 MHz
+		     if missing.
 
 Example:
 
@@ -18,6 +20,7 @@ ir0: ir@1c21800 {
 	compatible = "allwinner,sun4i-a10-ir";
 	clocks = <&apb0_gates 6>, <&ir0_clk>;
 	clock-names = "apb", "ir";
+	clock-frequency = <3000000>;
 	resets = <&apb0_rst 1>;
 	interrupts = <0 5 1>;
 	reg = <0x01C21800 0x40>;

+ 71 - 1
Documentation/media/kapi/cec-core.rst

@@ -110,11 +110,14 @@ your driver:
 		void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
 		void (*adap_free)(struct cec_adapter *adap);
 
+		/* Error injection callbacks */
+		...
+
 		/* High-level callbacks */
 		...
 	};
 
-The five low-level ops deal with various aspects of controlling the CEC adapter
+The seven low-level ops deal with various aspects of controlling the CEC adapter
 hardware:
 
 
@@ -286,6 +289,70 @@ handling the receive interrupt. The framework expects to see the cec_transmit_do
 call before the cec_received_msg call, otherwise it can get confused if the
 received message was in reply to the transmitted message.
 
+Optional: Implementing Error Injection Support
+----------------------------------------------
+
+If the CEC adapter supports Error Injection functionality, then that can
+be exposed through the Error Injection callbacks:
+
+.. code-block:: none
+
+	struct cec_adap_ops {
+		/* Low-level callbacks */
+		...
+
+		/* Error injection callbacks */
+		int (*error_inj_show)(struct cec_adapter *adap, struct seq_file *sf);
+		bool (*error_inj_parse_line)(struct cec_adapter *adap, char *line);
+
+		/* High-level CEC message callback */
+		...
+	};
+
+If both callbacks are set, then an ``error-inj`` file will appear in debugfs.
+The basic syntax is as follows:
+
+Leading spaces/tabs are ignored. If the next character is a ``#`` or the end of the
+line was reached, then the whole line is ignored. Otherwise a command is expected.
+
+This basic parsing is done in the CEC Framework. It is up to the driver to decide
+what commands to implement. The only requirement is that the command ``clear`` without
+any arguments must be implemented and that it will remove all current error injection
+commands.
+
+This ensures that you can always do ``echo clear >error-inj`` to clear any error
+injections without having to know the details of the driver-specific commands.
+
+Note that the output of ``error-inj`` shall be valid as input to ``error-inj``.
+So this must work:
+
+.. code-block:: none
+
+	$ cat error-inj >einj.txt
+	$ cat einj.txt >error-inj
+
+The first callback is called when this file is read and it should show the
+the current error injection state:
+
+.. c:function::
+	int (*error_inj_show)(struct cec_adapter *adap, struct seq_file *sf);
+
+It is recommended that it starts with a comment block with basic usage
+information. It returns 0 for success and an error otherwise.
+
+The second callback will parse commands written to the ``error-inj`` file:
+
+.. c:function::
+	bool (*error_inj_parse_line)(struct cec_adapter *adap, char *line);
+
+The ``line`` argument points to the start of the command. Any leading
+spaces or tabs have already been skipped. It is a single line only (so there
+are no embedded newlines) and it is 0-terminated. The callback is free to
+modify the contents of the buffer. It is only called for lines containing a
+command, so this callback is never called for empty lines or comment lines.
+
+Return true if the command was valid or false if there were syntax errors.
+
 Implementing the High-Level CEC Adapter
 ---------------------------------------
 
@@ -298,6 +365,9 @@ CEC protocol driven. The following high-level callbacks are available:
 		/* Low-level callbacks */
 		...
 
+		/* Error injection callbacks */
+		...
+
 		/* High-level CEC message callback */
 		int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
 	};

+ 1 - 0
Documentation/media/lirc.h.rst.exceptions

@@ -57,6 +57,7 @@ ignore symbol RC_PROTO_RC6_MCE
 ignore symbol RC_PROTO_SHARP
 ignore symbol RC_PROTO_XMP
 ignore symbol RC_PROTO_CEC
+ignore symbol RC_PROTO_IMON
 
 # Undocumented macros
 

+ 1 - 0
Documentation/media/uapi/cec/cec-api.rst

@@ -23,6 +23,7 @@ This part describes the CEC: Consumer Electronics Control
 
     cec-intro
     cec-funcs
+    cec-pin-error-inj
     cec-header
 
 

+ 325 - 0
Documentation/media/uapi/cec/cec-pin-error-inj.rst

@@ -0,0 +1,325 @@
+CEC Pin Framework Error Injection
+=================================
+
+The CEC Pin Framework is a core CEC framework for CEC hardware that only
+has low-level support for the CEC bus. Most hardware today will have
+high-level CEC support where the hardware deals with driving the CEC bus,
+but some older devices aren't that fancy. However, this framework also
+allows you to connect the CEC pin to a GPIO on e.g. a Raspberry Pi and
+you have now made a CEC adapter.
+
+What makes doing this so interesting is that since we have full control
+over the bus it is easy to support error injection. This is ideal to
+test how well CEC adapters can handle error conditions.
+
+Currently only the cec-gpio driver (when the CEC line is directly
+connected to a pull-up GPIO line) and the AllWinner A10/A20 drm driver
+support this framework.
+
+If ``CONFIG_CEC_PIN_ERROR_INJ`` is enabled, then error injection is available
+through debugfs. Specifically, in ``/sys/kernel/debug/cec/cecX/`` there is
+now an ``error-inj`` file.
+
+.. note::
+
+    The error injection commands are not a stable ABI and may change in the
+    future.
+
+With ``cat error-inj`` you can see both the possible commands and the current
+error injection status::
+
+	$ cat /sys/kernel/debug/cec/cec0/error-inj
+	# Clear error injections:
+	#   clear          clear all rx and tx error injections
+	#   rx-clear       clear all rx error injections
+	#   tx-clear       clear all tx error injections
+	#   <op> clear     clear all rx and tx error injections for <op>
+	#   <op> rx-clear  clear all rx error injections for <op>
+	#   <op> tx-clear  clear all tx error injections for <op>
+	#
+	# RX error injection:
+	#   <op>[,<mode>] rx-nack              NACK the message instead of sending an ACK
+	#   <op>[,<mode>] rx-low-drive <bit>   force a low-drive condition at this bit position
+	#   <op>[,<mode>] rx-add-byte          add a spurious byte to the received CEC message
+	#   <op>[,<mode>] rx-remove-byte       remove the last byte from the received CEC message
+	#   <op>[,<mode>] rx-arb-lost <poll>   generate a POLL message to trigger an arbitration lost
+	#
+	# TX error injection settings:
+	#   tx-ignore-nack-until-eom           ignore early NACKs until EOM
+	#   tx-custom-low-usecs <usecs>        define the 'low' time for the custom pulse
+	#   tx-custom-high-usecs <usecs>       define the 'high' time for the custom pulse
+	#   tx-custom-pulse                    transmit the custom pulse once the bus is idle
+	#
+	# TX error injection:
+	#   <op>[,<mode>] tx-no-eom            don't set the EOM bit
+	#   <op>[,<mode>] tx-early-eom         set the EOM bit one byte too soon
+	#   <op>[,<mode>] tx-add-bytes <num>   append <num> (1-255) spurious bytes to the message
+	#   <op>[,<mode>] tx-remove-byte       drop the last byte from the message
+	#   <op>[,<mode>] tx-short-bit <bit>   make this bit shorter than allowed
+	#   <op>[,<mode>] tx-long-bit <bit>    make this bit longer than allowed
+	#   <op>[,<mode>] tx-custom-bit <bit>  send the custom pulse instead of this bit
+	#   <op>[,<mode>] tx-short-start       send a start pulse that's too short
+	#   <op>[,<mode>] tx-long-start        send a start pulse that's too long
+	#   <op>[,<mode>] tx-custom-start      send the custom pulse instead of the start pulse
+	#   <op>[,<mode>] tx-last-bit <bit>    stop sending after this bit
+	#   <op>[,<mode>] tx-low-drive <bit>   force a low-drive condition at this bit position
+	#
+	# <op>       CEC message opcode (0-255) or 'any'
+	# <mode>     'once' (default), 'always', 'toggle' or 'off'
+	# <bit>      CEC message bit (0-159)
+	#            10 bits per 'byte': bits 0-7: data, bit 8: EOM, bit 9: ACK
+	# <poll>     CEC poll message used to test arbitration lost (0x00-0xff, default 0x0f)
+	# <usecs>    microseconds (0-10000000, default 1000)
+
+	clear
+
+You can write error injection commands to ``error-inj`` using
+``echo 'cmd' >error-inj`` or ``cat cmd.txt >error-inj``. The ``cat error-inj``
+output contains the current error commands. You can save the output to a file
+and use it as an input to ``error-inj`` later.
+
+Basic Syntax
+------------
+
+Leading spaces/tabs are ignored. If the next character is a ``#`` or the end
+of the line was reached, then the whole line is ignored. Otherwise a command
+is expected.
+
+The error injection commands fall in two main groups: those relating to
+receiving CEC messages and those relating to transmitting CEC messages. In
+addition, there are commands to clear existing error injection commands and
+to create custom pulses on the CEC bus.
+
+Most error injection commands can be executed for specific CEC opcodes or for
+all opcodes (``any``). Each command also has a 'mode' which can be ``off``
+(can be used to turn off an existing error injection command), ``once``
+(the default) which will trigger the error injection only once for the next
+received or transmitted message, ``always`` to always trigger the error
+injection and ``toggle`` to toggle the error injection on or off for every
+transmit or receive.
+
+So '``any rx-nack``' will NACK the next received CEC message,
+'``any,always rx-nack``' will NACK all received CEC messages and
+'``0x82,toggle rx-nack``' will only NACK if an Active Source message was
+received and do that only for every other received message.
+
+After an error was injected with mode ``once`` the error injection command
+is cleared automatically, so ``once`` is a one-time deal.
+
+All combinations of ``<op>`` and error injection commands can co-exist. So
+this is fine::
+
+	0x9e tx-add-bytes 1
+	0x9e tx-early-eom
+	0x9f tx-add-bytes 2
+	any rx-nack
+
+All four error injection commands will be active simultaneously.
+
+However, if the same ``<op>`` and command combination is specified,
+but with different arguments::
+
+	0x9e tx-add-bytes 1
+	0x9e tx-add-bytes 2
+
+Then the second will overwrite the first.
+
+Clear Error Injections
+----------------------
+
+``clear``
+    Clear all error injections.
+
+``rx-clear``
+    Clear all receive error injections
+
+``tx-clear``
+    Clear all transmit error injections
+
+``<op> clear``
+    Clear all error injections for the given opcode.
+
+``<op> rx-clear``
+    Clear all receive error injections for the given opcode.
+
+``<op> tx-clear``
+    Clear all transmit error injections for the given opcode.
+
+Receive Messages
+----------------
+
+``<op>[,<mode>] rx-nack``
+    NACK broadcast messages and messages directed to this CEC adapter.
+    Every byte of the message will be NACKed in case the transmitter
+    keeps transmitting after the first byte was NACKed.
+
+``<op>[,<mode>] rx-low-drive <bit>``
+    Force a Low Drive condition at this bit position. If <op> specifies
+    a specific CEC opcode then the bit position must be at least 18,
+    otherwise the opcode hasn't been received yet. This tests if the
+    transmitter can handle the Low Drive condition correctly and reports
+    the error correctly. Note that a Low Drive in the first 4 bits can also
+    be interpreted as an Arbitration Lost condition by the transmitter.
+    This is implementation dependent.
+
+``<op>[,<mode>] rx-add-byte``
+    Add a spurious 0x55 byte to the received CEC message, provided
+    the message was 15 bytes long or less. This is useful to test
+    the high-level protocol since spurious bytes should be ignored.
+
+``<op>[,<mode>] rx-remove-byte``
+    Remove the last byte from the received CEC message, provided it
+    was at least 2 bytes long. This is useful to test the high-level
+    protocol since messages that are too short should be ignored.
+
+``<op>[,<mode>] rx-arb-lost <poll>``
+    Generate a POLL message to trigger an Arbitration Lost condition.
+    This command is only allowed for ``<op>`` values of ``next`` or ``all``.
+    As soon as a start bit has been received the CEC adapter will switch
+    to transmit mode and it will transmit a POLL message. By default this is
+    0x0f, but it can also be specified explicitly via the ``<poll>`` argument.
+
+    This command can be used to test the Arbitration Lost condition in
+    the remote CEC transmitter. Arbitration happens when two CEC adapters
+    start sending a message at the same time. In that case the initiator
+    with the most leading zeroes wins and the other transmitter has to
+    stop transmitting ('Arbitration Lost'). This is very hard to test,
+    except by using this error injection command.
+
+    This does not work if the remote CEC transmitter has logical address
+    0 ('TV') since that will always win.
+
+Transmit Messages
+-----------------
+
+``tx-ignore-nack-until-eom``
+    This setting changes the behavior of transmitting CEC messages. Normally
+    as soon as the receiver NACKs a byte the transmit will stop, but the
+    specification also allows that the full message is transmitted and only
+    at the end will the transmitter look at the ACK bit. This is not
+    recommended behavior since there is no point in keeping the CEC bus busy
+    for longer than is strictly needed. Especially given how slow the bus is.
+
+    This setting can be used to test how well a receiver deals with
+    transmitters that ignore NACKs until the very end of the message.
+
+``<op>[,<mode>] tx-no-eom``
+    Don't set the EOM bit. Normally the last byte of the message has the EOM
+    (End-Of-Message) bit set. With this command the transmit will just stop
+    without ever sending an EOM. This can be used to test how a receiver
+    handles this case. Normally receivers have a time-out after which
+    they will go back to the Idle state.
+
+``<op>[,<mode>] tx-early-eom``
+    Set the EOM bit one byte too soon. This obviously only works for messages
+    of two bytes or more. The EOM bit will be set for the second-to-last byte
+    and not for the final byte. The receiver should ignore the last byte in
+    this case. Since the resulting message is likely to be too short for this
+    same reason the whole message is typically ignored. The receiver should be
+    in Idle state after the last byte was transmitted.
+
+``<op>[,<mode>] tx-add-bytes <num>``
+    Append ``<num>`` (1-255) spurious bytes to the message. The extra bytes
+    have the value of the byte position in the message. So if you transmit a
+    two byte message (e.g. a Get CEC Version message) and add 2 bytes, then
+    the full message received by the remote CEC adapter is
+    ``0x40 0x9f 0x02 0x03``.
+
+    This command can be used to test buffer overflows in the receiver. E.g.
+    what does it do when it receives more than the maximum message size of 16
+    bytes.
+
+``<op>[,<mode>] tx-remove-byte``
+    Drop the last byte from the message, provided the message is at least
+    two bytes long. The receiver should ignore messages that are too short.
+
+``<op>[,<mode>] tx-short-bit <bit>``
+    Make this bit period shorter than allowed. The bit position cannot be
+    an Ack bit.  If <op> specifies a specific CEC opcode then the bit position
+    must be at least 18, otherwise the opcode hasn't been received yet.
+    Normally the period of a data bit is between 2.05 and 2.75 milliseconds.
+    With this command the period of this bit is 1.8 milliseconds, this is
+    done by reducing the time the CEC bus is high. This bit period is less
+    than is allowed and the receiver should respond with a Low Drive
+    condition.
+
+    This command is ignored for 0 bits in bit positions 0 to 3. This is
+    because the receiver also looks for an Arbitration Lost condition in
+    those first four bits and it is undefined what will happen if it
+    sees a too-short 0 bit.
+
+``<op>[,<mode>] tx-long-bit <bit>``
+    Make this bit period longer than is valid. The bit position cannot be
+    an Ack bit.  If <op> specifies a specific CEC opcode then the bit position
+    must be at least 18, otherwise the opcode hasn't been received yet.
+    Normally the period of a data bit is between 2.05 and 2.75 milliseconds.
+    With this command the period of this bit is 2.9 milliseconds, this is
+    done by increasing the time the CEC bus is high.
+
+    Even though this bit period is longer than is valid it is undefined what
+    a receiver will do. It might just accept it, or it might time out and
+    return to Idle state. Unfortunately the CEC specification is silent about
+    this.
+
+    This command is ignored for 0 bits in bit positions 0 to 3. This is
+    because the receiver also looks for an Arbitration Lost condition in
+    those first four bits and it is undefined what will happen if it
+    sees a too-long 0 bit.
+
+``<op>[,<mode>] tx-short-start``
+    Make this start bit period shorter than allowed. Normally the period of
+    a start bit is between 4.3 and 4.7 milliseconds. With this command the
+    period of the start bit is 4.1 milliseconds, this is done by reducing
+    the time the CEC bus is high. This start bit period is less than is
+    allowed and the receiver should return to Idle state when this is detected.
+
+``<op>[,<mode>] tx-long-start``
+    Make this start bit period longer than is valid. Normally the period of
+    a start bit is between 4.3 and 4.7 milliseconds. With this command the
+    period of the start bit is 5 milliseconds, this is done by increasing
+    the time the CEC bus is high. This start bit period is more than is
+    valid and the receiver should return to Idle state when this is detected.
+
+    Even though this start bit period is longer than is valid it is undefined
+    what a receiver will do. It might just accept it, or it might time out and
+    return to Idle state. Unfortunately the CEC specification is silent about
+    this.
+
+``<op>[,<mode>] tx-last-bit <bit>``
+    Just stop transmitting after this bit.  If <op> specifies a specific CEC
+    opcode then the bit position must be at least 18, otherwise the opcode
+    hasn't been received yet. This command can be used to test how the receiver
+    reacts when a message just suddenly stops. It should time out and go back
+    to Idle state.
+
+``<op>[,<mode>] tx-low-drive <bit>``
+    Force a Low Drive condition at this bit position. If <op> specifies a
+    specific CEC opcode then the bit position must be at least 18, otherwise
+    the opcode hasn't been received yet. This can be used to test how the
+    receiver handles Low Drive conditions. Note that if this happens at bit
+    positions 0-3 the receiver can interpret this as an Arbitration Lost
+    condition. This is implementation dependent.
+
+Custom Pulses
+-------------
+
+``tx-custom-low-usecs <usecs>``
+    This defines the duration in microseconds that the custom pulse pulls
+    the CEC line low. The default is 1000 microseconds.
+
+``tx-custom-high-usecs <usecs>``
+    This defines the duration in microseconds that the custom pulse keeps the
+    CEC line high (unless another CEC adapter pulls it low in that time).
+    The default is 1000 microseconds. The total period of the custom pulse is
+    ``tx-custom-low-usecs + tx-custom-high-usecs``.
+
+``<op>[,<mode>] tx-custom-bit <bit>``
+    Send the custom bit instead of a regular data bit. The bit position cannot
+    be an Ack bit.  If <op> specifies a specific CEC opcode then the bit
+    position must be at least 18, otherwise the opcode hasn't been received yet.
+
+``<op>[,<mode>] tx-custom-start``
+    Send the custom bit instead of a regular start bit.
+
+``tx-custom-pulse``
+    Transmit a single custom pulse as soon as the CEC bus is idle.

+ 15 - 4
Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst

@@ -144,10 +144,21 @@ id's until they get an error.
 
     -  .. row 9
 
-       -  union
+       -  __u32
+
+       -  ``reserved[4]``
+
+       -
+       -
+       -  Reserved for future extensions. Drivers and applications must set
+          the array to zero.
 
     -  .. row 10
 
+       -  union
+
+    -  .. row 11
+
        -
        -  struct
 
@@ -156,7 +167,7 @@ id's until they get an error.
        -
        -  Valid for (sub-)devices that create a single device node.
 
-    -  .. row 11
+    -  .. row 12
 
        -
        -
@@ -166,7 +177,7 @@ id's until they get an error.
 
        -  Device node major number.
 
-    -  .. row 12
+    -  .. row 13
 
        -
        -
@@ -176,7 +187,7 @@ id's until they get an error.
 
        -  Device node minor number.
 
-    -  .. row 13
+    -  .. row 14
 
        -
        -  __u8

+ 18 - 0
Documentation/media/uapi/mediactl/media-ioc-enum-links.rst

@@ -125,6 +125,15 @@ returned during the enumeration process.
 
        -  Pad flags, see :ref:`media-pad-flag` for more details.
 
+    -  .. row 4
+
+       -  __u32
+
+       -  ``reserved[2]``
+
+       -  Reserved for future extensions. Drivers and applications must set
+          the array to zero.
+
 
 
 .. c:type:: media_link_desc
@@ -161,6 +170,15 @@ returned during the enumeration process.
 
        -  Link flags, see :ref:`media-link-flag` for more details.
 
+    -  .. row 4
+
+       -  __u32
+
+       -  ``reserved[4]``
+
+       -  Reserved for future extensions. Drivers and applications must set
+          the array to zero.
+
 
 Return Value
 ============

+ 43 - 11
Documentation/media/uapi/mediactl/media-ioc-g-topology.rst

@@ -68,7 +68,7 @@ desired arrays with the media graph elements.
 
     -  .. row 2
 
-       -  __u64
+       -  __u32
 
        -  ``num_entities``
 
@@ -76,6 +76,14 @@ desired arrays with the media graph elements.
 
     -  .. row 3
 
+       -  __u32
+
+       -  ``reserved1``
+
+       -  Applications and drivers shall set this to 0.
+
+    -  .. row 4
+
        -  __u64
 
        -  ``ptr_entities``
@@ -85,15 +93,23 @@ desired arrays with the media graph elements.
 	  the ioctl won't store the entities. It will just update
 	  ``num_entities``
 
-    -  .. row 4
+    -  .. row 5
 
-       -  __u64
+       -  __u32
 
        -  ``num_interfaces``
 
        -  Number of interfaces in the graph
 
-    -  .. row 5
+    -  .. row 6
+
+       -  __u32
+
+       -  ``reserved2``
+
+       -  Applications and drivers shall set this to 0.
+
+    -  .. row 7
 
        -  __u64
 
@@ -104,15 +120,23 @@ desired arrays with the media graph elements.
 	  the ioctl won't store the interfaces. It will just update
 	  ``num_interfaces``
 
-    -  .. row 6
+    -  .. row 8
 
-       -  __u64
+       -  __u32
 
        -  ``num_pads``
 
        -  Total number of pads in the graph
 
-    -  .. row 7
+    -  .. row 9
+
+       -  __u32
+
+       -  ``reserved3``
+
+       -  Applications and drivers shall set this to 0.
+
+    -  .. row 10
 
        -  __u64
 
@@ -122,15 +146,23 @@ desired arrays with the media graph elements.
 	  converted to a 64-bits integer. It can be zero. if zero, the ioctl
 	  won't store the pads. It will just update ``num_pads``
 
-    -  .. row 8
+    -  .. row 11
 
-       -  __u64
+       -  __u32
 
        -  ``num_links``
 
        -  Total number of data and interface links in the graph
 
-    -  .. row 9
+    -  .. row 12
+
+       -  __u32
+
+       -  ``reserved4``
+
+       -  Applications and drivers shall set this to 0.
+
+    -  .. row 13
 
        -  __u64
 
@@ -334,7 +366,7 @@ desired arrays with the media graph elements.
 
        -  On pad to pad links: unique ID for the source pad.
 
-	  On interface to entity links: unique ID for the entity.
+	  On interface to entity links: unique ID for the interface.
 
     -  .. row 3
 

+ 17 - 6
Documentation/media/uapi/mediactl/media-types.rst

@@ -26,7 +26,7 @@ Types and flags used to represent the media graph elements
 	  ``MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN``
 
        -  Unknown entity. That generally indicates that a driver didn't
-	  initialize properly the entity, with is a Kernel bug
+	  initialize properly the entity, which is a Kernel bug
 
     -  .. row 2
 
@@ -293,7 +293,7 @@ Types and flags used to represent the media graph elements
 
        -  ``MEDIA_ENT_F_PROC_VIDEO_STATISTICS``
 
-       -  Video statistics computation (histogram, 3A, ...). An entity
+       -  Video statistics computation (histogram, 3A, etc.). An entity
 	  capable of statistics computation must have one sink pad and
 	  one source pad. It computes statistics over the frames
 	  received on its sink pad and outputs the statistics data on
@@ -318,8 +318,19 @@ Types and flags used to represent the media graph elements
        - Video interface bridge. A video interface bridge entity must have at
          least one sink pad and at least one source pad. It receives video
          frames on its sink pad from an input video bus of one type (HDMI, eDP,
-         MIPI CSI-2, ...), and outputs them on its source pad to an output
-         video bus of another type (eDP, MIPI CSI-2, parallel, ...).
+         MIPI CSI-2, etc.), and outputs them on its source pad to an output
+         video bus of another type (eDP, MIPI CSI-2, parallel, etc.).
+
+    -  ..  row 31
+
+       ..  _MEDIA-ENT-F-DTV-DECODER:
+
+       -  ``MEDIA_ENT_F_DTV_DECODER``
+
+       -  Digital video decoder. The basic function of the video decoder is
+	  to accept digital video from a wide variety of sources
+	  and output it in some digital video standard, with appropriate
+	  timing signals.
 
 ..  tabularcolumns:: |p{5.5cm}|p{12.0cm}|
 
@@ -337,7 +348,7 @@ Types and flags used to represent the media graph elements
        -  ``MEDIA_ENT_FL_DEFAULT``
 
        -  Default entity for its type. Used to discover the default audio,
-	  VBI and video devices, the default camera sensor, ...
+	  VBI and video devices, the default camera sensor, etc.
 
     -  .. row 2
 
@@ -345,7 +356,7 @@ Types and flags used to represent the media graph elements
 
        -  ``MEDIA_ENT_FL_CONNECTOR``
 
-       -  The entity represents a data conector
+       -  The entity represents a connector.
 
 
 ..  tabularcolumns:: |p{6.5cm}|p{6.0cm}|p{5.0cm}|

+ 0 - 1
Documentation/media/uapi/rc/lirc-dev-intro.rst

@@ -18,7 +18,6 @@ Example dmesg output upon a driver registering w/LIRC:
 .. code-block:: none
 
     $ dmesg |grep lirc_dev
-    lirc_dev: IR Remote Control driver registered, major 248
     rc rc0: lirc_dev: driver mceusb registered at minor = 0
 
 What you should see for a chardev:

+ 1 - 1
Documentation/media/uapi/v4l/buffer.rst

@@ -13,7 +13,7 @@ Only pointers to buffers (planes) are exchanged, the data itself is not
 copied. These pointers, together with meta-information like timestamps
 or field parity, are stored in a struct :c:type:`v4l2_buffer`,
 argument to the :ref:`VIDIOC_QUERYBUF`,
-:ref:`VIDIOC_QBUF` and
+:ref:`VIDIOC_QBUF <VIDIOC_QBUF>` and
 :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl. In the multi-planar API,
 some plane-specific members of struct :c:type:`v4l2_buffer`,
 such as pointers and sizes for each plane, are stored in struct

+ 410 - 0
Documentation/media/uapi/v4l/extended-controls.rst

@@ -1960,6 +1960,416 @@ enum v4l2_vp8_golden_frame_sel -
     1, 2 and 3 corresponding to encoder profiles 0, 1, 2 and 3.
 
 
+High Efficiency Video Coding (HEVC/H.265) Control Reference
+-----------------------------------------------------------
+
+The HEVC/H.265 controls include controls for encoding parameters of HEVC/H.265
+video codec.
+
+
+.. _hevc-control-id:
+
+HEVC/H.265 Control IDs
+^^^^^^^^^^^^^^^^^^^^^^
+
+``V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (integer)``
+    Minimum quantization parameter for HEVC.
+    Valid range: from 0 to 51.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (integer)``
+    Maximum quantization parameter for HEVC.
+    Valid range: from 0 to 51.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (integer)``
+    Quantization parameter for an I frame for HEVC.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP (integer)``
+    Quantization parameter for a P frame for HEVC.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP (integer)``
+    Quantization parameter for a B frame for HEVC.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP (boolean)``
+    HIERARCHICAL_QP allows the host to specify the quantization parameter
+    values for each temporal layer through HIERARCHICAL_QP_LAYER. This is
+    valid only if HIERARCHICAL_CODING_LAYER is greater than 1. Setting the
+    control value to 1 enables setting of the QP values for the layers.
+
+.. _v4l2-hevc-hier-coding-type:
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE``
+    (enum)
+
+enum v4l2_mpeg_video_hevc_hier_coding_type -
+    Selects the hierarchical coding type for encoding. Possible values are:
+
+.. raw:: latex
+
+    \footnotesize
+
+.. tabularcolumns:: |p{9.0cm}|p{8.0cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B``
+      - Use the B frame for hierarchical coding.
+    * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P``
+      - Use the P frame for hierarchical coding.
+
+.. raw:: latex
+
+    \normalsize
+
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER (integer)``
+    Selects the hierarchical coding layer. In normal encoding
+    (non-hierarchial coding), it should be zero. Possible values are [0, 6].
+    0 indicates HIERARCHICAL CODING LAYER 0, 1 indicates HIERARCHICAL CODING
+    LAYER 1 and so on.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP (integer)``
+    Indicates quantization parameter for hierarchical coding layer 0.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP (integer)``
+    Indicates quantization parameter for hierarchical coding layer 1.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP (integer)``
+    Indicates quantization parameter for hierarchical coding layer 2.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP (integer)``
+    Indicates quantization parameter for hierarchical coding layer 3.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP (integer)``
+    Indicates quantization parameter for hierarchical coding layer 4.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP (integer)``
+    Indicates quantization parameter for hierarchical coding layer 5.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP (integer)``
+    Indicates quantization parameter for hierarchical coding layer 6.
+    Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+    V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP].
+
+.. _v4l2-hevc-profile:
+
+``V4L2_CID_MPEG_VIDEO_HEVC_PROFILE``
+    (enum)
+
+enum v4l2_mpeg_video_hevc_profile -
+    Select the desired profile for HEVC encoder.
+
+.. raw:: latex
+
+    \footnotesize
+
+.. tabularcolumns:: |p{9.0cm}|p{8.0cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN``
+      - Main profile.
+    * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE``
+      - Main still picture profile.
+    * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10``
+      - Main 10 profile.
+
+.. raw:: latex
+
+    \normalsize
+
+
+.. _v4l2-hevc-level:
+
+``V4L2_CID_MPEG_VIDEO_HEVC_LEVEL``
+    (enum)
+
+enum v4l2_mpeg_video_hevc_level -
+    Selects the desired level for HEVC encoder.
+
+.. raw:: latex
+
+    \footnotesize
+
+.. tabularcolumns:: |p{9.0cm}|p{8.0cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_1``
+      - Level 1.0
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2``
+      - Level 2.0
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1``
+      - Level 2.1
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3``
+      - Level 3.0
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1``
+      - Level 3.1
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4``
+      - Level 4.0
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1``
+      - Level 4.1
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5``
+      - Level 5.0
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1``
+      - Level 5.1
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2``
+      - Level 5.2
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6``
+      - Level 6.0
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1``
+      - Level 6.1
+    * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2``
+      - Level 6.2
+
+.. raw:: latex
+
+    \normalsize
+
+
+``V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION (integer)``
+    Indicates the number of evenly spaced subintervals, called ticks, within
+    one second. This is a 16 bit unsigned integer and has a maximum value up to
+    0xffff and a minimum value of 1.
+
+.. _v4l2-hevc-tier:
+
+``V4L2_CID_MPEG_VIDEO_HEVC_TIER``
+    (enum)
+
+enum v4l2_mpeg_video_hevc_tier -
+    TIER_FLAG specifies tiers information of the HEVC encoded picture. Tier
+    were made to deal with applications that differ in terms of maximum bit
+    rate. Setting the flag to 0 selects HEVC tier as Main tier and setting
+    this flag to 1 indicates High tier. High tier is for applications requiring
+    high bit rates.
+
+.. raw:: latex
+
+    \footnotesize
+
+.. tabularcolumns:: |p{9.0cm}|p{8.0cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    * - ``V4L2_MPEG_VIDEO_HEVC_TIER_MAIN``
+      - Main tier.
+    * - ``V4L2_MPEG_VIDEO_HEVC_TIER_HIGH``
+      - High tier.
+
+.. raw:: latex
+
+    \normalsize
+
+
+``V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH (integer)``
+    Selects HEVC maximum coding unit depth.
+
+.. _v4l2-hevc-loop-filter-mode:
+
+``V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE``
+    (enum)
+
+enum v4l2_mpeg_video_hevc_loop_filter_mode -
+    Loop filter mode for HEVC encoder. Possible values are:
+
+.. raw:: latex
+
+    \footnotesize
+
+.. tabularcolumns:: |p{10.7cm}|p{6.3cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED``
+      - Loop filter is disabled.
+    * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED``
+      - Loop filter is enabled.
+    * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY``
+      - Loop filter is disabled at the slice boundary.
+
+.. raw:: latex
+
+    \normalsize
+
+
+``V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2 (integer)``
+    Selects HEVC loop filter beta offset. The valid range is [-6, +6].
+
+``V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2 (integer)``
+    Selects HEVC loop filter tc offset. The valid range is [-6, +6].
+
+.. _v4l2-hevc-refresh-type:
+
+``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE``
+    (enum)
+
+enum v4l2_mpeg_video_hevc_hier_refresh_type -
+    Selects refresh type for HEVC encoder.
+    Host has to specify the period into
+    V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD.
+
+.. raw:: latex
+
+    \footnotesize
+
+.. tabularcolumns:: |p{8.0cm}|p{9.0cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE``
+      - Use the B frame for hierarchical coding.
+    * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA``
+      - Use CRA (Clean Random Access Unit) picture encoding.
+    * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR``
+      - Use IDR (Instantaneous Decoding Refresh) picture encoding.
+
+.. raw:: latex
+
+    \normalsize
+
+
+``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD (integer)``
+    Selects the refresh period for HEVC encoder.
+    This specifies the number of I pictures between two CRA/IDR pictures.
+    This is valid only if REFRESH_TYPE is not 0.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU (boolean)``
+    Indicates HEVC lossless encoding. Setting it to 0 disables lossless
+    encoding. Setting it to 1 enables lossless encoding.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED (boolean)``
+    Indicates constant intra prediction for HEVC encoder. Specifies the
+    constrained intra prediction in which intra largest coding unit (LCU)
+    prediction is performed by using residual data and decoded samples of
+    neighboring intra LCU only. Setting the value to 1 enables constant intra
+    prediction and setting the value to 0 disables constant intra prediction.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT (boolean)``
+    Indicates wavefront parallel processing for HEVC encoder. Setting it to 0
+    disables the feature and setting it to 1 enables the wavefront parallel
+    processing.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB (boolean)``
+    Setting the value to 1 enables combination of P and B frame for HEVC
+    encoder.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID (boolean)``
+    Indicates temporal identifier for HEVC encoder which is enabled by
+    setting the value to 1.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING (boolean)``
+    Indicates bi-linear interpolation is conditionally used in the intra
+    prediction filtering process in the CVS when set to 1. Indicates bi-linear
+    interpolation is not used in the CVS when set to 0.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1 (integer)``
+    Indicates maximum number of merge candidate motion vectors.
+    Values are from 0 to 4.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION (boolean)``
+    Indicates temporal motion vector prediction for HEVC encoder. Setting it to
+    1 enables the prediction. Setting it to 0 disables the prediction.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE (boolean)``
+    Specifies if HEVC generates a stream with a size of the length field
+    instead of start code pattern. The size of the length field is configurable
+    through the V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD control. Setting
+    the value to 0 disables encoding without startcode pattern. Setting the
+    value to 1 will enables encoding without startcode pattern.
+
+.. _v4l2-hevc-size-of-length-field:
+
+``V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD``
+(enum)
+
+enum v4l2_mpeg_video_hevc_size_of_length_field -
+    Indicates the size of length field.
+    This is valid when encoding WITHOUT_STARTCODE_ENABLE is enabled.
+
+.. raw:: latex
+
+    \footnotesize
+
+.. tabularcolumns:: |p{6.0cm}|p{11.0cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_0``
+      - Generate start code pattern (Normal).
+    * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_1``
+      - Generate size of length field instead of start code pattern and length is 1.
+    * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_2``
+      - Generate size of length field instead of start code pattern and length is 2.
+    * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_4``
+      - Generate size of length field instead of start code pattern and length is 4.
+
+.. raw:: latex
+
+    \normalsize
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR (integer)``
+    Indicates bit rate for hierarchical coding layer 0 for HEVC encoder.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR (integer)``
+    Indicates bit rate for hierarchical coding layer 1 for HEVC encoder.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR (integer)``
+    Indicates bit rate for hierarchical coding layer 2 for HEVC encoder.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR (integer)``
+    Indicates bit rate for hierarchical coding layer 3 for HEVC encoder.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR (integer)``
+    Indicates bit rate for hierarchical coding layer 4 for HEVC encoder.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR (integer)``
+    Indicates bit rate for hierarchical coding layer 5 for HEVC encoder.
+
+``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR (integer)``
+    Indicates bit rate for hierarchical coding layer 6 for HEVC encoder.
+
+``V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES (integer)``
+    Selects number of P reference pictures required for HEVC encoder.
+    P-Frame can use 1 or 2 frames for reference.
+
+``V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR (integer)``
+    Indicates whether to generate SPS and PPS at every IDR. Setting it to 0
+    disables generating SPS and PPS at every IDR. Setting it to one enables
+    generating SPS and PPS at every IDR.
+
+
 .. _camera-controls:
 
 Camera Control Reference

+ 4 - 4
Documentation/media/uapi/v4l/func-poll.rst

@@ -39,7 +39,7 @@ When streaming I/O has been negotiated this function waits until a
 buffer has been filled by the capture device and can be dequeued with
 the :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl. For output devices this
 function waits until the device is ready to accept a new buffer to be
-queued up with the :ref:`VIDIOC_QBUF` ioctl for
+queued up with the :ref:`VIDIOC_QBUF <VIDIOC_QBUF>` ioctl for
 display. When buffers are already in the outgoing queue of the driver
 (capture) or the incoming queue isn't full (display) the function
 returns immediately.
@@ -52,11 +52,11 @@ flags in the ``revents`` field, output devices the ``POLLOUT`` and
 ``POLLWRNORM`` flags. When the function timed out it returns a value of
 zero, on failure it returns -1 and the ``errno`` variable is set
 appropriately. When the application did not call
-:ref:`VIDIOC_STREAMON` the :ref:`poll() <func-poll>`
+:ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` the :ref:`poll() <func-poll>`
 function succeeds, but sets the ``POLLERR`` flag in the ``revents``
 field. When the application has called
-:ref:`VIDIOC_STREAMON` for a capture device but
-hasn't yet called :ref:`VIDIOC_QBUF`, the
+:ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` for a capture device but
+hasn't yet called :ref:`VIDIOC_QBUF <VIDIOC_QBUF>`, the
 :ref:`poll() <func-poll>` function succeeds and sets the ``POLLERR`` flag in
 the ``revents`` field. For output devices this same situation will cause
 :ref:`poll() <func-poll>` to succeed as well, but it sets the ``POLLOUT`` and

+ 5 - 0
Documentation/media/uapi/v4l/pixfmt-compressed.rst

@@ -90,3 +90,8 @@ Compressed Formats
       - ``V4L2_PIX_FMT_VP9``
       - 'VP90'
       - VP9 video elementary stream.
+    * .. _V4L2-PIX-FMT-HEVC:
+
+      - ``V4L2_PIX_FMT_HEVC``
+      - 'HEVC'
+      - HEVC/H.265 video elementary stream.

+ 1 - 1
Documentation/media/uapi/v4l/pixfmt-v4l2.rst

@@ -40,7 +40,7 @@ Single-planar format structure
 	RGB formats in :ref:`rgb-formats`, YUV formats in
 	:ref:`yuv-formats`, and reserved codes in
 	:ref:`reserved-formats`
-    * - enum :c:type::`v4l2_field`
+    * - enum :c:type:`v4l2_field`
       - ``field``
       - Video images are typically interlaced. Applications can request to
 	capture or output only the top or bottom field, or both fields

+ 6 - 2
Documentation/media/uapi/v4l/subdev-formats.rst

@@ -16,10 +16,14 @@ Media Bus Formats
 
     * - __u32
       - ``width``
-      - Image width, in pixels.
+      - Image width in pixels.
     * - __u32
       - ``height``
-      - Image height, in pixels.
+      - Image height in pixels. If ``field`` is one of ``V4L2_FIELD_TOP``,
+	``V4L2_FIELD_BOTTOM`` or ``V4L2_FIELD_ALTERNATE`` then height
+	refers to the number of lines in the field, otherwise it refers to
+	the number of lines in the frame (which is twice the field height
+	for interlaced formats).
     * - __u32
       - ``code``
       - Format code, from enum

+ 4 - 3
Documentation/media/uapi/v4l/vidioc-g-parm.rst

@@ -66,7 +66,7 @@ union holding separate parameters for input and output devices.
       -
       - The buffer (stream) type, same as struct
 	:c:type:`v4l2_format` ``type``, set by the
-	application. See :c:type:`v4l2_buf_type`
+	application. See :c:type:`v4l2_buf_type`.
     * - union
       - ``parm``
       -
@@ -75,12 +75,13 @@ union holding separate parameters for input and output devices.
       - struct :c:type:`v4l2_captureparm`
       - ``capture``
       - Parameters for capture devices, used when ``type`` is
-	``V4L2_BUF_TYPE_VIDEO_CAPTURE``.
+	``V4L2_BUF_TYPE_VIDEO_CAPTURE`` or
+	``V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE``.
     * -
       - struct :c:type:`v4l2_outputparm`
       - ``output``
       - Parameters for output devices, used when ``type`` is
-	``V4L2_BUF_TYPE_VIDEO_OUTPUT``.
+	``V4L2_BUF_TYPE_VIDEO_OUTPUT`` or ``V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE``.
     * -
       - __u8
       - ``raw_data``\ [200]

+ 1 - 1
Documentation/media/uapi/v4l/vidioc-prepare-buf.rst

@@ -34,7 +34,7 @@ Description
 
 Applications can optionally call the :ref:`VIDIOC_PREPARE_BUF` ioctl to
 pass ownership of the buffer to the driver before actually enqueuing it,
-using the :ref:`VIDIOC_QBUF` ioctl, and to prepare it for future I/O. Such
+using the :ref:`VIDIOC_QBUF <VIDIOC_QBUF>` ioctl, and to prepare it for future I/O. Such
 preparations may include cache invalidation or cleaning. Performing them
 in advance saves time during the actual I/O. In case such cache
 operations are not required, the application can use one of

+ 14 - 12
Documentation/media/v4l-drivers/imx.rst

@@ -109,7 +109,7 @@ imx6-mipi-csi2
 This is the MIPI CSI-2 receiver entity. It has one sink pad to receive
 the MIPI CSI-2 stream (usually from a MIPI CSI-2 camera sensor). It has
 four source pads, corresponding to the four MIPI CSI-2 demuxed virtual
-channel outputs. Multpiple source pads can be enabled to independently
+channel outputs. Multiple source pads can be enabled to independently
 stream from multiple virtual channels.
 
 This entity actually consists of two sub-blocks. One is the MIPI CSI-2
@@ -213,9 +213,11 @@ To give an example of crop and /2 downscale, this will crop a
 1280x960 input frame to 640x480, and then /2 downscale in both
 dimensions to 320x240 (assumes ipu1_csi0 is linked to ipu1_csi0_mux):
 
-media-ctl -V "'ipu1_csi0_mux':2[fmt:UYVY2X8/1280x960]"
-media-ctl -V "'ipu1_csi0':0[crop:(0,0)/640x480]"
-media-ctl -V "'ipu1_csi0':0[compose:(0,0)/320x240]"
+.. code-block:: none
+
+   media-ctl -V "'ipu1_csi0_mux':2[fmt:UYVY2X8/1280x960]"
+   media-ctl -V "'ipu1_csi0':0[crop:(0,0)/640x480]"
+   media-ctl -V "'ipu1_csi0':0[compose:(0,0)/320x240]"
 
 Frame Skipping in ipuX_csiY
 ---------------------------
@@ -229,8 +231,10 @@ at the source pad.
 The following example reduces an assumed incoming 60 Hz frame
 rate by half at the IDMAC output source pad:
 
-media-ctl -V "'ipu1_csi0':0[fmt:UYVY2X8/640x480@1/60]"
-media-ctl -V "'ipu1_csi0':2[fmt:UYVY2X8/640x480@1/30]"
+.. code-block:: none
+
+   media-ctl -V "'ipu1_csi0':0[fmt:UYVY2X8/640x480@1/60]"
+   media-ctl -V "'ipu1_csi0':2[fmt:UYVY2X8/640x480@1/30]"
 
 Frame Interval Monitor in ipuX_csiY
 -----------------------------------
@@ -422,8 +426,7 @@ This pipeline uses the preprocess encode entity to route frames directly
 from the CSI to the IC, to carry out scaling up to 1024x1024 resolution,
 CSC, flipping, and image rotation:
 
--> ipuX_csiY:1 -> 0:ipuX_ic_prp:1 -> 0:ipuX_ic_prpenc:1 ->
-   ipuX_ic_prpenc capture
+-> ipuX_csiY:1 -> 0:ipuX_ic_prp:1 -> 0:ipuX_ic_prpenc:1 -> ipuX_ic_prpenc capture
 
 Motion Compensated De-interlace:
 --------------------------------
@@ -432,8 +435,7 @@ This pipeline routes frames from the CSI direct pad to the VDIC entity to
 support motion-compensated de-interlacing (high motion mode only),
 scaling up to 1024x1024, CSC, flip, and rotation:
 
--> ipuX_csiY:1 -> 0:ipuX_vdic:2 -> 0:ipuX_ic_prp:2 ->
-   0:ipuX_ic_prpvf:1 -> ipuX_ic_prpvf capture
+-> ipuX_csiY:1 -> 0:ipuX_vdic:2 -> 0:ipuX_ic_prp:2 -> 0:ipuX_ic_prpvf:1 -> ipuX_ic_prpvf capture
 
 
 Usage Notes
@@ -458,8 +460,8 @@ This platform requires the OmniVision OV5642 module with a parallel
 camera interface, and the OV5640 module with a MIPI CSI-2
 interface. Both modules are available from Boundary Devices:
 
-https://boundarydevices.com/product/nit6x_5mp
-https://boundarydevices.com/product/nit6x_5mp_mipi
+- https://boundarydevices.com/product/nit6x_5mp
+- https://boundarydevices.com/product/nit6x_5mp_mipi
 
 Note that if only one camera module is available, the other sensor
 node can be disabled in the device tree.

+ 90 - 2
MAINTAINERS

@@ -3275,6 +3275,7 @@ F:	include/media/cec-notifier.h
 F:	include/uapi/linux/cec.h
 F:	include/uapi/linux/cec-funcs.h
 F:	Documentation/devicetree/bindings/media/cec.txt
+F:	Documentation/ABI/testing/debugfs-cec-error-inj
 
 CEC GPIO DRIVER
 M:	Hans Verkuil <hans.verkuil@cisco.com>
@@ -6865,6 +6866,13 @@ M:	James Hogan <jhogan@kernel.org>
 S:	Maintained
 F:	drivers/media/rc/img-ir/
 
+IMON SOUNDGRAPH USB IR RECEIVER
+M:	Sean Young <sean@mess.org>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	drivers/media/rc/imon_raw.c
+F:	drivers/media/rc/imon.c
+
 IMS TWINTURBO FRAMEBUFFER DRIVER
 L:	linux-fbdev@vger.kernel.org
 S:	Orphan
@@ -8604,6 +8612,14 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Supported
 F:	drivers/media/dvb-frontends/ascot2e*
 
+MEDIA DRIVERS FOR CXD2099AR CI CONTROLLERS
+M:	Jasmin Jessich <jasmin@anw.at>
+L:	linux-media@vger.kernel.org
+W:	https://linuxtv.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	drivers/media/dvb-frontends/cxd2099*
+
 MEDIA DRIVERS FOR CXD2841ER
 M:	Sergey Kozlov <serjk@netup.ru>
 M:	Abylay Ospan <aospan@netup.ru>
@@ -8614,6 +8630,15 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Supported
 F:	drivers/media/dvb-frontends/cxd2841er*
 
+MEDIA DRIVERS FOR CXD2880
+M:	Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+T:	git git://linuxtv.org/media_tree.git
+S:	Supported
+F:	drivers/media/dvb-frontends/cxd2880/*
+F:	drivers/media/spi/cxd2880*
+
 MEDIA DRIVERS FOR DIGITAL DEVICES PCIE DEVICES
 M:	Daniel Scheller <d.scheller.oss@gmail.com>
 L:	linux-media@vger.kernel.org
@@ -8681,6 +8706,16 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Supported
 F:	drivers/media/pci/netup_unidvb/*
 
+MEDIA DRIVERS FOR RENESAS - CEU
+M:	Jacopo Mondi <jacopo@jmondi.org>
+L:	linux-media@vger.kernel.org
+L:	linux-renesas-soc@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Supported
+F:	Documentation/devicetree/bindings/media/renesas,ceu.txt
+F:	drivers/media/platform/renesas-ceu.c
+F:	include/media/drv-intf/renesas-ceu.h
+
 MEDIA DRIVERS FOR RENESAS - DRIF
 M:	Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
 L:	linux-media@vger.kernel.org
@@ -9365,6 +9400,14 @@ S:	Maintained
 F:	drivers/media/i2c/mt9t001.c
 F:	include/media/i2c/mt9t001.h
 
+MT9T112 APTINA CAMERA SENSOR
+M:	Jacopo Mondi <jacopo@jmondi.org>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Odd Fixes
+F:	drivers/media/i2c/mt9t112.c
+F:	include/media/i2c/mt9t112.h
+
 MT9V032 APTINA CAMERA SENSOR
 M:	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
 L:	linux-media@vger.kernel.org
@@ -10170,6 +10213,13 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Maintained
 F:	drivers/media/i2c/ov13858.c
 
+OMNIVISION OV2685 SENSOR DRIVER
+M:	Shunqian Zheng <zhengsq@rock-chips.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	drivers/media/i2c/ov2685.c
+
 OMNIVISION OV5640 SENSOR DRIVER
 M:	Steve Longerbeam <slongerbeam@gmail.com>
 L:	linux-media@vger.kernel.org
@@ -10184,6 +10234,13 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Maintained
 F:	drivers/media/i2c/ov5647.c
 
+OMNIVISION OV5695 SENSOR DRIVER
+M:	Shunqian Zheng <zhengsq@rock-chips.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	drivers/media/i2c/ov5695.c
+
 OMNIVISION OV7670 SENSOR DRIVER
 M:	Jonathan Corbet <corbet@lwn.net>
 L:	linux-media@vger.kernel.org
@@ -10192,6 +10249,14 @@ S:	Maintained
 F:	drivers/media/i2c/ov7670.c
 F:	Documentation/devicetree/bindings/media/i2c/ov7670.txt
 
+OMNIVISION OV772x SENSOR DRIVER
+M:	Jacopo Mondi <jacopo@jmondi.org>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Odd fixes
+F:	drivers/media/i2c/ov772x.c
+F:	include/media/i2c/ov772x.h
+
 OMNIVISION OV7740 SENSOR DRIVER
 M:	Wenyou Yang <wenyou.yang@microchip.com>
 L:	linux-media@vger.kernel.org
@@ -10200,6 +10265,16 @@ S:	Maintained
 F:	drivers/media/i2c/ov7740.c
 F:	Documentation/devicetree/bindings/media/i2c/ov7740.txt
 
+OMNIVISION OV9650 SENSOR DRIVER
+M:	Sakari Ailus <sakari.ailus@linux.intel.com>
+R:	Akinobu Mita <akinobu.mita@gmail.com>
+R:	Sylwester Nawrocki <s.nawrocki@samsung.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	drivers/media/i2c/ov9650.c
+F:	Documentation/devicetree/bindings/media/i2c/ov9650.txt
+
 ONENAND FLASH DRIVER
 M:	Kyungmin Park <kyungmin.park@samsung.com>
 L:	linux-mtd@lists.infradead.org
@@ -12780,10 +12855,9 @@ S:	Maintained
 F:	drivers/net/ethernet/smsc/smsc9420.*
 
 SOC-CAMERA V4L2 SUBSYSTEM
-M:	Guennadi Liakhovetski <g.liakhovetski@gmx.de>
 L:	linux-media@vger.kernel.org
 T:	git git://linuxtv.org/media_tree.git
-S:	Maintained
+S:	Orphan
 F:	include/media/soc*
 F:	drivers/media/i2c/soc_camera/
 F:	drivers/media/platform/soc_camera/
@@ -13512,6 +13586,14 @@ T:	git git://linuxtv.org/mkrufky/tuners.git
 S:	Maintained
 F:	drivers/media/tuners/tda18271*
 
+TDA1997x MEDIA DRIVER
+M:	Tim Harvey <tharvey@gateworks.com>
+L:	linux-media@vger.kernel.org
+W:	https://linuxtv.org
+Q:	http://patchwork.linuxtv.org/project/linux-media/list/
+S:	Maintained
+F:	drivers/media/i2c/tda1997x.*
+
 TDA827x MEDIA DRIVER
 M:	Michael Krufky <mkrufky@linuxtv.org>
 L:	linux-media@vger.kernel.org
@@ -13593,6 +13675,12 @@ L:	linux-media@vger.kernel.org
 S:	Maintained
 F:	drivers/media/rc/ttusbir.c
 
+TECHWELL TW9910 VIDEO DECODER
+L:	linux-media@vger.kernel.org
+S:	Orphan
+F:	drivers/media/i2c/tw9910.c
+F:	include/media/i2c/tw9910.h
+
 TEE SUBSYSTEM
 M:	Jens Wiklander <jens.wiklander@linaro.org>
 S:	Maintained

+ 169 - 169
arch/sh/boards/mach-ecovec24/setup.c

@@ -7,44 +7,47 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
-
-#include <linux/init.h>
+#include <asm/clock.h>
+#include <asm/heartbeat.h>
+#include <asm/suspend.h>
+#include <cpu/sh7724.h>
+#include <linux/delay.h>
 #include <linux/device.h>
-#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/input/sh_keysc.h>
+#include <linux/interrupt.h>
+#include <linux/memblock.h>
+#include <linux/mfd/tmio.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/sh_mmcif.h>
 #include <linux/mtd/physmap.h>
-#include <linux/mfd/tmio.h>
 #include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/delay.h>
+#include <linux/gpio/machine.h>
+#include <linux/platform_data/gpio_backlight.h>
+#include <linux/platform_data/tsc2007.h>
+#include <linux/platform_device.h>
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
-#include <linux/usb/r8a66597.h>
-#include <linux/usb/renesas_usbhs.h>
-#include <linux/i2c.h>
-#include <linux/platform_data/tsc2007.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/sh_msiof.h>
-#include <linux/spi/mmc_spi.h>
-#include <linux/input.h>
-#include <linux/input/sh_keysc.h>
-#include <linux/platform_data/gpio_backlight.h>
 #include <linux/sh_eth.h>
 #include <linux/sh_intc.h>
+#include <linux/spi/mmc_spi.h>
+#include <linux/spi/sh_msiof.h>
+#include <linux/spi/spi.h>
+#include <linux/usb/r8a66597.h>
+#include <linux/usb/renesas_usbhs.h>
 #include <linux/videodev2.h>
-#include <video/sh_mobile_lcdc.h>
+
+#include <media/drv-intf/renesas-ceu.h>
+#include <media/i2c/mt9t112.h>
+#include <media/i2c/tw9910.h>
+
 #include <sound/sh_fsi.h>
 #include <sound/simple_card.h>
-#include <media/drv-intf/sh_mobile_ceu.h>
-#include <media/soc_camera.h>
-#include <media/i2c/tw9910.h>
-#include <media/i2c/mt9t112.h>
-#include <asm/heartbeat.h>
-#include <asm/clock.h>
-#include <asm/suspend.h>
-#include <cpu/sh7724.h>
+
+#include <video/sh_mobile_lcdc.h>
 
 /*
  *  Address      Interface        BusWidth
@@ -81,6 +84,10 @@
  * amixer set 'Out Mixer Right DAC Right' on
  */
 
+#define CEU_BUFFER_MEMORY_SIZE		(4 << 20)
+static phys_addr_t ceu0_dma_membase;
+static phys_addr_t ceu1_dma_membase;
+
 /* Heartbeat */
 static unsigned char led_pos[] = { 0, 1, 2, 3 };
 
@@ -382,8 +389,24 @@ static struct platform_device gpio_backlight_device = {
 };
 
 /* CEU0 */
-static struct sh_mobile_ceu_info sh_mobile_ceu0_info = {
-	.flags = SH_CEU_FLAG_USE_8BIT_BUS,
+static struct ceu_platform_data ceu0_pdata = {
+	.num_subdevs			= 2,
+	.subdevs = {
+		{ /* [0] = mt9t112  */
+			.flags		= 0,
+			.bus_width	= 8,
+			.bus_shift	= 0,
+			.i2c_adapter_id	= 0,
+			.i2c_address	= 0x3c,
+		},
+		{ /* [1] = tw9910  */
+			.flags		= 0,
+			.bus_width	= 8,
+			.bus_shift	= 0,
+			.i2c_adapter_id	= 0,
+			.i2c_address	= 0x45,
+		},
+	},
 };
 
 static struct resource ceu0_resources[] = {
@@ -397,24 +420,30 @@ static struct resource ceu0_resources[] = {
 		.start  = evt2irq(0x880),
 		.flags  = IORESOURCE_IRQ,
 	},
-	[2] = {
-		/* place holder for contiguous memory */
-	},
 };
 
 static struct platform_device ceu0_device = {
-	.name		= "sh_mobile_ceu",
-	.id             = 0, /* "ceu0" clock */
+	.name		= "renesas-ceu",
+	.id             = 0, /* ceu.0 */
 	.num_resources	= ARRAY_SIZE(ceu0_resources),
 	.resource	= ceu0_resources,
 	.dev	= {
-		.platform_data	= &sh_mobile_ceu0_info,
+		.platform_data	= &ceu0_pdata,
 	},
 };
 
 /* CEU1 */
-static struct sh_mobile_ceu_info sh_mobile_ceu1_info = {
-	.flags = SH_CEU_FLAG_USE_8BIT_BUS,
+static struct ceu_platform_data ceu1_pdata = {
+	.num_subdevs			= 1,
+	.subdevs = {
+		{ /* [0] = mt9t112  */
+			.flags		= 0,
+			.bus_width	= 8,
+			.bus_shift	= 0,
+			.i2c_adapter_id	= 1,
+			.i2c_address	= 0x3c,
+		},
+	},
 };
 
 static struct resource ceu1_resources[] = {
@@ -428,26 +457,71 @@ static struct resource ceu1_resources[] = {
 		.start  = evt2irq(0x9e0),
 		.flags  = IORESOURCE_IRQ,
 	},
-	[2] = {
-		/* place holder for contiguous memory */
-	},
 };
 
 static struct platform_device ceu1_device = {
-	.name		= "sh_mobile_ceu",
-	.id             = 1, /* "ceu1" clock */
+	.name		= "renesas-ceu",
+	.id             = 1, /* ceu.1 */
 	.num_resources	= ARRAY_SIZE(ceu1_resources),
 	.resource	= ceu1_resources,
 	.dev	= {
-		.platform_data	= &sh_mobile_ceu1_info,
+		.platform_data	= &ceu1_pdata,
+	},
+};
+
+/* Power up/down GPIOs for camera devices and video decoder */
+static struct gpiod_lookup_table tw9910_gpios = {
+	.dev_id		= "0-0045",
+	.table		= {
+		GPIO_LOOKUP("sh7724_pfc", GPIO_PTU2, "pdn", GPIO_ACTIVE_HIGH),
+	},
+};
+
+static struct gpiod_lookup_table mt9t112_0_gpios = {
+	.dev_id		= "0-003c",
+	.table		= {
+		GPIO_LOOKUP("sh7724_pfc", GPIO_PTA3, "standby",
+			    GPIO_ACTIVE_HIGH),
+	},
+};
+
+static struct gpiod_lookup_table mt9t112_1_gpios = {
+	.dev_id		= "1-003c",
+	.table		= {
+		GPIO_LOOKUP("sh7724_pfc", GPIO_PTA4, "standby",
+			    GPIO_ACTIVE_HIGH),
 	},
 };
 
 /* I2C device */
+static struct tw9910_video_info tw9910_info = {
+	.buswidth	= 8,
+	.mpout		= TW9910_MPO_FIELD,
+};
+
+static struct mt9t112_platform_data mt9t112_0_pdata = {
+	.flags = MT9T112_FLAG_PCLK_RISING_EDGE,
+	.divider = { 0x49, 0x6, 0, 6, 0, 9, 9, 6, 0 }, /* for 24MHz */
+};
+
+static struct mt9t112_platform_data mt9t112_1_pdata = {
+	.flags = MT9T112_FLAG_PCLK_RISING_EDGE,
+	.divider = { 0x49, 0x6, 0, 6, 0, 9, 9, 6, 0 }, /* for 24MHz */
+};
+
 static struct i2c_board_info i2c0_devices[] = {
 	{
 		I2C_BOARD_INFO("da7210", 0x1a),
 	},
+	{
+		I2C_BOARD_INFO("tw9910", 0x45),
+		.platform_data = &tw9910_info,
+	},
+	{
+		/* 1st camera */
+		I2C_BOARD_INFO("mt9t112", 0x3c),
+		.platform_data = &mt9t112_0_pdata,
+	},
 };
 
 static struct i2c_board_info i2c1_devices[] = {
@@ -457,7 +531,12 @@ static struct i2c_board_info i2c1_devices[] = {
 	{
 		I2C_BOARD_INFO("lis3lv02d", 0x1c),
 		.irq = evt2irq(0x620),
-	}
+	},
+	{
+		/* 2nd camera */
+		I2C_BOARD_INFO("mt9t112", 0x3c),
+		.platform_data = &mt9t112_1_pdata,
+	},
 };
 
 /* KEYSC */
@@ -724,115 +803,6 @@ static struct platform_device msiof0_device = {
 
 #endif
 
-/* I2C Video/Camera */
-static struct i2c_board_info i2c_camera[] = {
-	{
-		I2C_BOARD_INFO("tw9910", 0x45),
-	},
-	{
-		/* 1st camera */
-		I2C_BOARD_INFO("mt9t112", 0x3c),
-	},
-	{
-		/* 2nd camera */
-		I2C_BOARD_INFO("mt9t112", 0x3c),
-	},
-};
-
-/* tw9910 */
-static int tw9910_power(struct device *dev, int mode)
-{
-	int val = mode ? 0 : 1;
-
-	gpio_set_value(GPIO_PTU2, val);
-	if (mode)
-		mdelay(100);
-
-	return 0;
-}
-
-static struct tw9910_video_info tw9910_info = {
-	.buswidth	= SOCAM_DATAWIDTH_8,
-	.mpout		= TW9910_MPO_FIELD,
-};
-
-static struct soc_camera_link tw9910_link = {
-	.i2c_adapter_id	= 0,
-	.bus_id		= 1,
-	.power		= tw9910_power,
-	.board_info	= &i2c_camera[0],
-	.priv		= &tw9910_info,
-};
-
-/* mt9t112 */
-static int mt9t112_power1(struct device *dev, int mode)
-{
-	gpio_set_value(GPIO_PTA3, mode);
-	if (mode)
-		mdelay(100);
-
-	return 0;
-}
-
-static struct mt9t112_camera_info mt9t112_info1 = {
-	.flags = MT9T112_FLAG_PCLK_RISING_EDGE | MT9T112_FLAG_DATAWIDTH_8,
-	.divider = { 0x49, 0x6, 0, 6, 0, 9, 9, 6, 0 }, /* for 24MHz */
-};
-
-static struct soc_camera_link mt9t112_link1 = {
-	.i2c_adapter_id	= 0,
-	.power		= mt9t112_power1,
-	.bus_id		= 0,
-	.board_info	= &i2c_camera[1],
-	.priv		= &mt9t112_info1,
-};
-
-static int mt9t112_power2(struct device *dev, int mode)
-{
-	gpio_set_value(GPIO_PTA4, mode);
-	if (mode)
-		mdelay(100);
-
-	return 0;
-}
-
-static struct mt9t112_camera_info mt9t112_info2 = {
-	.flags = MT9T112_FLAG_PCLK_RISING_EDGE | MT9T112_FLAG_DATAWIDTH_8,
-	.divider = { 0x49, 0x6, 0, 6, 0, 9, 9, 6, 0 }, /* for 24MHz */
-};
-
-static struct soc_camera_link mt9t112_link2 = {
-	.i2c_adapter_id	= 1,
-	.power		= mt9t112_power2,
-	.bus_id		= 1,
-	.board_info	= &i2c_camera[2],
-	.priv		= &mt9t112_info2,
-};
-
-static struct platform_device camera_devices[] = {
-	{
-		.name	= "soc-camera-pdrv",
-		.id	= 0,
-		.dev	= {
-			.platform_data = &tw9910_link,
-		},
-	},
-	{
-		.name	= "soc-camera-pdrv",
-		.id	= 1,
-		.dev	= {
-			.platform_data = &mt9t112_link1,
-		},
-	},
-	{
-		.name	= "soc-camera-pdrv",
-		.id	= 2,
-		.dev	= {
-			.platform_data = &mt9t112_link2,
-		},
-	},
-};
-
 /* FSI */
 static struct resource fsi_resources[] = {
 	[0] = {
@@ -979,6 +949,11 @@ static struct platform_device sh_mmcif_device = {
 };
 #endif
 
+static struct platform_device *ecovec_ceu_devices[] __initdata = {
+	&ceu0_device,
+	&ceu1_device,
+};
+
 static struct platform_device *ecovec_devices[] __initdata = {
 	&heartbeat_device,
 	&nor_flash_device,
@@ -988,8 +963,6 @@ static struct platform_device *ecovec_devices[] __initdata = {
 	&usbhs_device,
 	&lcdc_device,
 	&gpio_backlight_device,
-	&ceu0_device,
-	&ceu1_device,
 	&keysc_device,
 	&cn12_power,
 #if defined(CONFIG_MMC_SDHI) || defined(CONFIG_MMC_SDHI_MODULE)
@@ -1001,9 +974,6 @@ static struct platform_device *ecovec_devices[] __initdata = {
 #else
 	&msiof0_device,
 #endif
-	&camera_devices[0],
-	&camera_devices[1],
-	&camera_devices[2],
 	&fsi_device,
 	&fsi_da7210_device,
 	&irda_device,
@@ -1240,7 +1210,6 @@ static int __init arch_setup(void)
 	gpio_request(GPIO_FN_VIO0_CLK, NULL);
 	gpio_request(GPIO_FN_VIO0_FLD, NULL);
 	gpio_request(GPIO_FN_VIO0_HD,  NULL);
-	platform_resource_setup_memory(&ceu0_device, "ceu0", 4 << 20);
 
 	/* enable CEU1 */
 	gpio_request(GPIO_FN_VIO1_D7,  NULL);
@@ -1255,7 +1224,6 @@ static int __init arch_setup(void)
 	gpio_request(GPIO_FN_VIO1_HD,  NULL);
 	gpio_request(GPIO_FN_VIO1_VD,  NULL);
 	gpio_request(GPIO_FN_VIO1_CLK, NULL);
-	platform_resource_setup_memory(&ceu1_device, "ceu1", 4 << 20);
 
 	/* enable KEYSC */
 	gpio_request(GPIO_FN_KEYOUT5_IN5, NULL);
@@ -1332,16 +1300,6 @@ static int __init arch_setup(void)
 		__raw_writew((__raw_readw(IODRIVEA) & ~0x3000) | 0x2000,
 			     IODRIVEA);
 
-	/* enable Video */
-	gpio_request(GPIO_PTU2, NULL);
-	gpio_direction_output(GPIO_PTU2, 1);
-
-	/* enable Camera */
-	gpio_request(GPIO_PTA3, NULL);
-	gpio_request(GPIO_PTA4, NULL);
-	gpio_direction_output(GPIO_PTA3, 0);
-	gpio_direction_output(GPIO_PTA4, 0);
-
 	/* enable FSI */
 	gpio_request(GPIO_FN_FSIMCKB,    NULL);
 	gpio_request(GPIO_FN_FSIIBSD,    NULL);
@@ -1390,6 +1348,11 @@ static int __init arch_setup(void)
 	gpio_request(GPIO_PTU5, NULL);
 	gpio_direction_output(GPIO_PTU5, 0);
 
+	/* Register gpio lookup tables for cameras and video decoder */
+	gpiod_add_lookup_table(&tw9910_gpios);
+	gpiod_add_lookup_table(&mt9t112_0_gpios);
+	gpiod_add_lookup_table(&mt9t112_1_gpios);
+
 	/* enable I2C device */
 	i2c_register_board_info(0, i2c0_devices,
 				ARRAY_SIZE(i2c0_devices));
@@ -1431,6 +1394,25 @@ static int __init arch_setup(void)
 	gpio_set_value(GPIO_PTG4, 1);
 #endif
 
+	/* Initialize CEU platform devices separately to map memory first */
+	device_initialize(&ecovec_ceu_devices[0]->dev);
+	arch_setup_pdev_archdata(ecovec_ceu_devices[0]);
+	dma_declare_coherent_memory(&ecovec_ceu_devices[0]->dev,
+				    ceu0_dma_membase, ceu0_dma_membase,
+				    ceu0_dma_membase +
+				    CEU_BUFFER_MEMORY_SIZE - 1,
+				    DMA_MEMORY_EXCLUSIVE);
+	platform_device_add(ecovec_ceu_devices[0]);
+
+	device_initialize(&ecovec_ceu_devices[1]->dev);
+	arch_setup_pdev_archdata(ecovec_ceu_devices[1]);
+	dma_declare_coherent_memory(&ecovec_ceu_devices[1]->dev,
+				    ceu1_dma_membase, ceu1_dma_membase,
+				    ceu1_dma_membase +
+				    CEU_BUFFER_MEMORY_SIZE - 1,
+				    DMA_MEMORY_EXCLUSIVE);
+	platform_device_add(ecovec_ceu_devices[1]);
+
 	return platform_add_devices(ecovec_devices,
 				    ARRAY_SIZE(ecovec_devices));
 }
@@ -1443,6 +1425,24 @@ static int __init devices_setup(void)
 }
 device_initcall(devices_setup);
 
+/* Reserve a portion of memory for CEU 0 and CEU 1 buffers */
+static void __init ecovec_mv_mem_reserve(void)
+{
+	phys_addr_t phys;
+	phys_addr_t size = CEU_BUFFER_MEMORY_SIZE;
+
+	phys = memblock_alloc_base(size, PAGE_SIZE, MEMBLOCK_ALLOC_ANYWHERE);
+	memblock_free(phys, size);
+	memblock_remove(phys, size);
+	ceu0_dma_membase = phys;
+
+	phys = memblock_alloc_base(size, PAGE_SIZE, MEMBLOCK_ALLOC_ANYWHERE);
+	memblock_free(phys, size);
+	memblock_remove(phys, size);
+	ceu1_dma_membase = phys;
+}
+
 static struct sh_machine_vector mv_ecovec __initmv = {
 	.mv_name	= "R0P7724 (EcoVec)",
+	.mv_mem_reserve	= ecovec_mv_mem_reserve,
 };

+ 100 - 125
arch/sh/boards/mach-migor/setup.c

@@ -1,17 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Renesas System Solutions Asia Pte. Ltd - Migo-R
  *
  * Copyright (C) 2008 Magnus Damm
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
  */
+#include <linux/clkdev.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/input.h>
 #include <linux/input/sh_keysc.h>
+#include <linux/memblock.h>
 #include <linux/mmc/host.h>
 #include <linux/mtd/physmap.h>
 #include <linux/mfd/tmio.h>
@@ -23,10 +22,11 @@
 #include <linux/delay.h>
 #include <linux/clk.h>
 #include <linux/gpio.h>
+#include <linux/gpio/machine.h>
 #include <linux/videodev2.h>
 #include <linux/sh_intc.h>
 #include <video/sh_mobile_lcdc.h>
-#include <media/drv-intf/sh_mobile_ceu.h>
+#include <media/drv-intf/renesas-ceu.h>
 #include <media/i2c/ov772x.h>
 #include <media/soc_camera.h>
 #include <media/i2c/tw9910.h>
@@ -45,6 +45,9 @@
  * 0x18000000       8GB    8   NAND Flash (K9K8G08U0A)
  */
 
+#define CEU_BUFFER_MEMORY_SIZE		(4 << 20)
+static phys_addr_t ceu_dma_membase;
+
 static struct smc91x_platdata smc91x_info = {
 	.flags = SMC91X_USE_16BIT | SMC91X_NOWAIT,
 };
@@ -301,65 +304,24 @@ static struct platform_device migor_lcdc_device = {
 	},
 };
 
-static struct clk *camera_clk;
-static DEFINE_MUTEX(camera_lock);
-
-static void camera_power_on(int is_tw)
-{
-	mutex_lock(&camera_lock);
-
-	/* Use 10 MHz VIO_CKO instead of 24 MHz to work
-	 * around signal quality issues on Panel Board V2.1.
-	 */
-	camera_clk = clk_get(NULL, "video_clk");
-	clk_set_rate(camera_clk, 10000000);
-	clk_enable(camera_clk);	/* start VIO_CKO */
-
-	/* use VIO_RST to take camera out of reset */
-	mdelay(10);
-	if (is_tw) {
-		gpio_set_value(GPIO_PTT2, 0);
-		gpio_set_value(GPIO_PTT0, 0);
-	} else {
-		gpio_set_value(GPIO_PTT0, 1);
-	}
-	gpio_set_value(GPIO_PTT3, 0);
-	mdelay(10);
-	gpio_set_value(GPIO_PTT3, 1);
-	mdelay(10); /* wait to let chip come out of reset */
-}
-
-static void camera_power_off(void)
-{
-	clk_disable(camera_clk); /* stop VIO_CKO */
-	clk_put(camera_clk);
-
-	gpio_set_value(GPIO_PTT3, 0);
-	mutex_unlock(&camera_lock);
-}
-
-static int ov7725_power(struct device *dev, int mode)
-{
-	if (mode)
-		camera_power_on(0);
-	else
-		camera_power_off();
-
-	return 0;
-}
-
-static int tw9910_power(struct device *dev, int mode)
-{
-	if (mode)
-		camera_power_on(1);
-	else
-		camera_power_off();
-
-	return 0;
-}
-
-static struct sh_mobile_ceu_info sh_mobile_ceu_info = {
-	.flags = SH_CEU_FLAG_USE_8BIT_BUS,
+static struct ceu_platform_data ceu_pdata = {
+	.num_subdevs			= 2,
+	.subdevs = {
+		{ /* [0] = ov772x */
+			.flags		= 0,
+			.bus_width	= 8,
+			.bus_shift	= 0,
+			.i2c_adapter_id	= 0,
+			.i2c_address	= 0x21,
+		},
+		{ /* [1] = tw9910 */
+			.flags		= 0,
+			.bus_width	= 8,
+			.bus_shift	= 0,
+			.i2c_adapter_id	= 0,
+			.i2c_address	= 0x45,
+		},
+	},
 };
 
 static struct resource migor_ceu_resources[] = {
@@ -373,18 +335,32 @@ static struct resource migor_ceu_resources[] = {
 		.start  = evt2irq(0x880),
 		.flags  = IORESOURCE_IRQ,
 	},
-	[2] = {
-		/* place holder for contiguous memory */
-	},
 };
 
 static struct platform_device migor_ceu_device = {
-	.name		= "sh_mobile_ceu",
-	.id             = 0, /* "ceu0" clock */
+	.name		= "renesas-ceu",
+	.id             = 0, /* ceu.0 */
 	.num_resources	= ARRAY_SIZE(migor_ceu_resources),
 	.resource	= migor_ceu_resources,
 	.dev	= {
-		.platform_data	= &sh_mobile_ceu_info,
+		.platform_data	= &ceu_pdata,
+	},
+};
+
+/* Powerdown/reset gpios for CEU image sensors */
+static struct gpiod_lookup_table ov7725_gpios = {
+	.dev_id		= "0-0021",
+	.table		= {
+		GPIO_LOOKUP("sh7722_pfc", GPIO_PTT0, "pwdn", GPIO_ACTIVE_HIGH),
+		GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "rstb", GPIO_ACTIVE_LOW),
+	},
+};
+
+static struct gpiod_lookup_table tw9910_gpios = {
+	.dev_id		= "0-0045",
+	.table		= {
+		GPIO_LOOKUP("sh7722_pfc", GPIO_PTT2, "pdn", GPIO_ACTIVE_HIGH),
+		GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "rstb", GPIO_ACTIVE_LOW),
 	},
 };
 
@@ -423,6 +399,15 @@ static struct platform_device sdhi_cn9_device = {
 	},
 };
 
+static struct ov772x_camera_info ov7725_info = {
+	.flags		= 0,
+};
+
+static struct tw9910_video_info tw9910_info = {
+	.buswidth       = 8,
+	.mpout          = TW9910_MPO_FIELD,
+};
+
 static struct i2c_board_info migor_i2c_devices[] = {
 	{
 		I2C_BOARD_INFO("rs5c372b", 0x32),
@@ -434,51 +419,13 @@ static struct i2c_board_info migor_i2c_devices[] = {
 	{
 		I2C_BOARD_INFO("wm8978", 0x1a),
 	},
-};
-
-static struct i2c_board_info migor_i2c_camera[] = {
 	{
 		I2C_BOARD_INFO("ov772x", 0x21),
+		.platform_data = &ov7725_info,
 	},
 	{
 		I2C_BOARD_INFO("tw9910", 0x45),
-	},
-};
-
-static struct ov772x_camera_info ov7725_info;
-
-static struct soc_camera_link ov7725_link = {
-	.power		= ov7725_power,
-	.board_info	= &migor_i2c_camera[0],
-	.i2c_adapter_id	= 0,
-	.priv		= &ov7725_info,
-};
-
-static struct tw9910_video_info tw9910_info = {
-	.buswidth	= SOCAM_DATAWIDTH_8,
-	.mpout		= TW9910_MPO_FIELD,
-};
-
-static struct soc_camera_link tw9910_link = {
-	.power		= tw9910_power,
-	.board_info	= &migor_i2c_camera[1],
-	.i2c_adapter_id	= 0,
-	.priv		= &tw9910_info,
-};
-
-static struct platform_device migor_camera[] = {
-	{
-		.name	= "soc-camera-pdrv",
-		.id	= 0,
-		.dev	= {
-			.platform_data = &ov7725_link,
-		},
-	}, {
-		.name	= "soc-camera-pdrv",
-		.id	= 1,
-		.dev	= {
-			.platform_data = &tw9910_link,
-		},
+		.platform_data = &tw9910_info,
 	},
 };
 
@@ -486,12 +433,9 @@ static struct platform_device *migor_devices[] __initdata = {
 	&smc91x_eth_device,
 	&sh_keysc_device,
 	&migor_lcdc_device,
-	&migor_ceu_device,
 	&migor_nor_flash_device,
 	&migor_nand_flash_device,
 	&sdhi_cn9_device,
-	&migor_camera[0],
-	&migor_camera[1],
 };
 
 extern char migor_sdram_enter_start;
@@ -501,6 +445,8 @@ extern char migor_sdram_leave_end;
 
 static int __init migor_devices_setup(void)
 {
+	struct clk *video_clk;
+
 	/* register board specific self-refresh code */
 	sh_mobile_register_self_refresh(SUSP_SH_STANDBY | SUSP_SH_SF,
 					&migor_sdram_enter_start,
@@ -620,20 +566,8 @@ static int __init migor_devices_setup(void)
 	gpio_request(GPIO_FN_VIO_D9, NULL);
 	gpio_request(GPIO_FN_VIO_D8, NULL);
 
-	gpio_request(GPIO_PTT3, NULL); /* VIO_RST */
-	gpio_direction_output(GPIO_PTT3, 0);
-	gpio_request(GPIO_PTT2, NULL); /* TV_IN_EN */
-	gpio_direction_output(GPIO_PTT2, 1);
-	gpio_request(GPIO_PTT0, NULL); /* CAM_EN */
-#ifdef CONFIG_SH_MIGOR_RTA_WVGA
-	gpio_direction_output(GPIO_PTT0, 0);
-#else
-	gpio_direction_output(GPIO_PTT0, 1);
-#endif
 	__raw_writew(__raw_readw(PORT_MSELCRB) | 0x2000, PORT_MSELCRB); /* D15->D8 */
 
-	platform_resource_setup_memory(&migor_ceu_device, "ceu", 4 << 20);
-
 	/* SIU: Port B */
 	gpio_request(GPIO_FN_SIUBOLR, NULL);
 	gpio_request(GPIO_FN_SIUBOBT, NULL);
@@ -647,9 +581,36 @@ static int __init migor_devices_setup(void)
 	 */
 	__raw_writew(__raw_readw(PORT_MSELCRA) | 1, PORT_MSELCRA);
 
+	 /*
+	  * Use 10 MHz VIO_CKO instead of 24 MHz to work around signal quality
+	  * issues on Panel Board V2.1.
+	  */
+	video_clk = clk_get(NULL, "video_clk");
+	if (!IS_ERR(video_clk)) {
+		clk_set_rate(video_clk, clk_round_rate(video_clk, 10000000));
+		clk_put(video_clk);
+	}
+
+	/* Add a clock alias for ov7725 xclk source. */
+	clk_add_alias("xclk", "0-0021", "video_clk", NULL);
+
+	/* Register GPIOs for video sources. */
+	gpiod_add_lookup_table(&ov7725_gpios);
+	gpiod_add_lookup_table(&tw9910_gpios);
+
 	i2c_register_board_info(0, migor_i2c_devices,
 				ARRAY_SIZE(migor_i2c_devices));
 
+	/* Initialize CEU platform device separately to map memory first */
+	device_initialize(&migor_ceu_device.dev);
+	arch_setup_pdev_archdata(&migor_ceu_device);
+	dma_declare_coherent_memory(&migor_ceu_device.dev,
+				    ceu_dma_membase, ceu_dma_membase,
+				    ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1,
+				    DMA_MEMORY_EXCLUSIVE);
+
+	platform_device_add(&migor_ceu_device);
+
 	return platform_add_devices(migor_devices, ARRAY_SIZE(migor_devices));
 }
 arch_initcall(migor_devices_setup);
@@ -665,10 +626,24 @@ static int migor_mode_pins(void)
 	return MODE_PIN0 | MODE_PIN1 | MODE_PIN5;
 }
 
+/* Reserve a portion of memory for CEU buffers */
+static void __init migor_mv_mem_reserve(void)
+{
+	phys_addr_t phys;
+	phys_addr_t size = CEU_BUFFER_MEMORY_SIZE;
+
+	phys = memblock_alloc_base(size, PAGE_SIZE, MEMBLOCK_ALLOC_ANYWHERE);
+	memblock_free(phys, size);
+	memblock_remove(phys, size);
+
+	ceu_dma_membase = phys;
+}
+
 /*
  * The Machine Vector
  */
 static struct sh_machine_vector mv_migor __initmv = {
 	.mv_name		= "Migo-R",
 	.mv_mode_pins		= migor_mode_pins,
+	.mv_mem_reserve		= migor_mv_mem_reserve,
 };

+ 1 - 1
arch/sh/kernel/cpu/sh4a/clock-sh7722.c

@@ -223,7 +223,7 @@ static struct clk_lookup lookups[] = {
 	CLKDEV_DEV_ID("sh-vou.0", &mstp_clks[HWBLK_VOU]),
 	CLKDEV_CON_ID("jpu0", &mstp_clks[HWBLK_JPU]),
 	CLKDEV_CON_ID("beu0", &mstp_clks[HWBLK_BEU]),
-	CLKDEV_DEV_ID("sh_mobile_ceu.0", &mstp_clks[HWBLK_CEU]),
+	CLKDEV_DEV_ID("renesas-ceu.0", &mstp_clks[HWBLK_CEU]),
 	CLKDEV_CON_ID("veu0", &mstp_clks[HWBLK_VEU]),
 	CLKDEV_CON_ID("vpu0", &mstp_clks[HWBLK_VPU]),
 	CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[HWBLK_LCDC]),

+ 2 - 2
arch/sh/kernel/cpu/sh4a/clock-sh7724.c

@@ -338,14 +338,14 @@ static struct clk_lookup lookups[] = {
 	CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[HWBLK_SDHI0]),
 	CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[HWBLK_SDHI1]),
 	CLKDEV_CON_ID("veu1", &mstp_clks[HWBLK_VEU1]),
-	CLKDEV_DEV_ID("sh_mobile_ceu.1", &mstp_clks[HWBLK_CEU1]),
+	CLKDEV_DEV_ID("renesas-ceu.1", &mstp_clks[HWBLK_CEU1]),
 	CLKDEV_CON_ID("beu1", &mstp_clks[HWBLK_BEU1]),
 	CLKDEV_CON_ID("2ddmac0", &mstp_clks[HWBLK_2DDMAC]),
 	CLKDEV_DEV_ID("sh_fsi.0", &mstp_clks[HWBLK_SPU]),
 	CLKDEV_CON_ID("jpu0", &mstp_clks[HWBLK_JPU]),
 	CLKDEV_DEV_ID("sh-vou", &mstp_clks[HWBLK_VOU]),
 	CLKDEV_CON_ID("beu0", &mstp_clks[HWBLK_BEU0]),
-	CLKDEV_DEV_ID("sh_mobile_ceu.0", &mstp_clks[HWBLK_CEU0]),
+	CLKDEV_DEV_ID("renesas-ceu.0", &mstp_clks[HWBLK_CEU0]),
 	CLKDEV_CON_ID("veu0", &mstp_clks[HWBLK_VEU0]),
 	CLKDEV_CON_ID("vpu0", &mstp_clks[HWBLK_VPU]),
 	CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[HWBLK_LCDC]),

+ 177 - 1
drivers/input/touchscreen/sur40.c

@@ -38,6 +38,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 #include <media/videobuf2-v4l2.h>
 #include <media/videobuf2-dma-sg.h>
 
@@ -81,7 +82,10 @@ struct sur40_blob {
 
 	__le32 area;       /* size in pixels/pressure (?) */
 
-	u8 padding[32];
+	u8 padding[24];
+
+	__le32 tag_id;     /* valid when type == 0x04 (SUR40_TAG) */
+	__le32 unknown;
 
 } __packed;
 
@@ -146,6 +150,40 @@ struct sur40_image_header {
 #define SUR40_TOUCH	0x02
 #define SUR40_TAG	0x04
 
+/* video controls */
+#define SUR40_BRIGHTNESS_MAX 0xff
+#define SUR40_BRIGHTNESS_MIN 0x00
+#define SUR40_BRIGHTNESS_DEF 0xff
+
+#define SUR40_CONTRAST_MAX 0x0f
+#define SUR40_CONTRAST_MIN 0x00
+#define SUR40_CONTRAST_DEF 0x0a
+
+#define SUR40_GAIN_MAX 0x09
+#define SUR40_GAIN_MIN 0x00
+#define SUR40_GAIN_DEF 0x08
+
+#define SUR40_BACKLIGHT_MAX 0x01
+#define SUR40_BACKLIGHT_MIN 0x00
+#define SUR40_BACKLIGHT_DEF 0x01
+
+#define sur40_str(s) #s
+#define SUR40_PARAM_RANGE(lo, hi) " (range " sur40_str(lo) "-" sur40_str(hi) ")"
+
+/* module parameters */
+static uint brightness = SUR40_BRIGHTNESS_DEF;
+module_param(brightness, uint, 0644);
+MODULE_PARM_DESC(brightness, "set initial brightness"
+	SUR40_PARAM_RANGE(SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX));
+static uint contrast = SUR40_CONTRAST_DEF;
+module_param(contrast, uint, 0644);
+MODULE_PARM_DESC(contrast, "set initial contrast"
+	SUR40_PARAM_RANGE(SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX));
+static uint gain = SUR40_GAIN_DEF;
+module_param(gain, uint, 0644);
+MODULE_PARM_DESC(gain, "set initial gain"
+	SUR40_PARAM_RANGE(SUR40_GAIN_MIN, SUR40_GAIN_MAX));
+
 static const struct v4l2_pix_format sur40_pix_format[] = {
 	{
 		.pixelformat = V4L2_TCH_FMT_TU08,
@@ -178,6 +216,7 @@ struct sur40_state {
 	struct video_device vdev;
 	struct mutex lock;
 	struct v4l2_pix_format pix_fmt;
+	struct v4l2_ctrl_handler hdl;
 
 	struct vb2_queue queue;
 	struct list_head buf_list;
@@ -187,6 +226,7 @@ struct sur40_state {
 	struct sur40_data *bulk_in_buffer;
 	size_t bulk_in_size;
 	u8 bulk_in_epaddr;
+	u8 vsvideo;
 
 	char phys[64];
 };
@@ -200,6 +240,11 @@ struct sur40_buffer {
 static const struct video_device sur40_video_device;
 static const struct vb2_queue sur40_queue;
 static void sur40_process_video(struct sur40_state *sur40);
+static int sur40_s_ctrl(struct v4l2_ctrl *ctrl);
+
+static const struct v4l2_ctrl_ops sur40_ctrl_ops = {
+	.s_ctrl = sur40_s_ctrl,
+};
 
 /*
  * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
@@ -220,6 +265,81 @@ static int sur40_command(struct sur40_state *dev,
 			       0x00, index, buffer, size, 1000);
 }
 
+/* poke a byte in the panel register space */
+static int sur40_poke(struct sur40_state *dev, u8 offset, u8 value)
+{
+	int result;
+	u8 index = 0x96; // 0xae for permanent write
+
+	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+		0x32, index, NULL, 0, 1000);
+	if (result < 0)
+		goto error;
+	msleep(5);
+
+	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+		0x72, offset, NULL, 0, 1000);
+	if (result < 0)
+		goto error;
+	msleep(5);
+
+	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+		0xb2, value, NULL, 0, 1000);
+	if (result < 0)
+		goto error;
+	msleep(5);
+
+error:
+	return result;
+}
+
+static int sur40_set_preprocessor(struct sur40_state *dev, u8 value)
+{
+	u8 setting_07[2] = { 0x01, 0x00 };
+	u8 setting_17[2] = { 0x85, 0x80 };
+	int result;
+
+	if (value > 1)
+		return -ERANGE;
+
+	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+		0x07, setting_07[value], NULL, 0, 1000);
+	if (result < 0)
+		goto error;
+	msleep(5);
+
+	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+		0x17, setting_17[value], NULL, 0, 1000);
+	if (result < 0)
+		goto error;
+	msleep(5);
+
+error:
+	return result;
+}
+
+static void sur40_set_vsvideo(struct sur40_state *handle, u8 value)
+{
+	int i;
+
+	for (i = 0; i < 4; i++)
+		sur40_poke(handle, 0x1c+i, value);
+	handle->vsvideo = value;
+}
+
+static void sur40_set_irlevel(struct sur40_state *handle, u8 value)
+{
+	int i;
+
+	for (i = 0; i < 8; i++)
+		sur40_poke(handle, 0x08+(2*i), value);
+}
+
 /* Initialization routine, called from sur40_open */
 static int sur40_init(struct sur40_state *dev)
 {
@@ -631,6 +751,36 @@ static int sur40_probe(struct usb_interface *interface,
 	sur40->vdev.queue = &sur40->queue;
 	video_set_drvdata(&sur40->vdev, sur40);
 
+	/* initialize the control handler for 4 controls */
+	v4l2_ctrl_handler_init(&sur40->hdl, 4);
+	sur40->v4l2.ctrl_handler = &sur40->hdl;
+	sur40->vsvideo = (SUR40_CONTRAST_DEF << 4) | SUR40_GAIN_DEF;
+
+	v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_BRIGHTNESS,
+	  SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX, 1, clamp(brightness,
+	  (uint)SUR40_BRIGHTNESS_MIN, (uint)SUR40_BRIGHTNESS_MAX));
+
+	v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_CONTRAST,
+	  SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX, 1, clamp(contrast,
+	  (uint)SUR40_CONTRAST_MIN, (uint)SUR40_CONTRAST_MAX));
+
+	v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_GAIN,
+	  SUR40_GAIN_MIN, SUR40_GAIN_MAX, 1, clamp(gain,
+	  (uint)SUR40_GAIN_MIN, (uint)SUR40_GAIN_MAX));
+
+	v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops,
+	  V4L2_CID_BACKLIGHT_COMPENSATION, SUR40_BACKLIGHT_MIN,
+	  SUR40_BACKLIGHT_MAX, 1, SUR40_BACKLIGHT_DEF);
+
+	v4l2_ctrl_handler_setup(&sur40->hdl);
+
+	if (sur40->hdl.error) {
+		dev_err(&interface->dev,
+			"Unable to register video controls.");
+		v4l2_ctrl_handler_free(&sur40->hdl);
+		goto err_unreg_v4l2;
+	}
+
 	error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1);
 	if (error) {
 		dev_err(&interface->dev,
@@ -663,6 +813,7 @@ static void sur40_disconnect(struct usb_interface *interface)
 {
 	struct sur40_state *sur40 = usb_get_intfdata(interface);
 
+	v4l2_ctrl_handler_free(&sur40->hdl);
 	video_unregister_device(&sur40->vdev);
 	v4l2_device_unregister(&sur40->v4l2);
 
@@ -856,6 +1007,31 @@ static int sur40_vidioc_g_fmt(struct file *file, void *priv,
 	return 0;
 }
 
+static int sur40_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct sur40_state *sur40  = container_of(ctrl->handler,
+	  struct sur40_state, hdl);
+	u8 value = sur40->vsvideo;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		sur40_set_irlevel(sur40, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		value = (value & 0x0f) | (ctrl->val << 4);
+		sur40_set_vsvideo(sur40, value);
+		break;
+	case V4L2_CID_GAIN:
+		value = (value & 0xf0) | (ctrl->val);
+		sur40_set_vsvideo(sur40, value);
+		break;
+	case V4L2_CID_BACKLIGHT_COMPENSATION:
+		sur40_set_preprocessor(sur40, ctrl->val);
+		break;
+	}
+	return 0;
+}
+
 static int sur40_ioctl_parm(struct file *file, void *priv,
 			    struct v4l2_streamparm *p)
 {

+ 1 - 0
drivers/media/Kconfig

@@ -141,6 +141,7 @@ config DVB_CORE
 	tristate
 	depends on MEDIA_SUPPORT
 	depends on MEDIA_DIGITAL_TV_SUPPORT
+	depends on (I2C || I2C=n)
 	default y
 	select CRC32
 

+ 6 - 0
drivers/media/cec/Kconfig

@@ -4,3 +4,9 @@ config MEDIA_CEC_RC
 	depends on CEC_CORE=m || RC_CORE=y
 	---help---
 	  Pass on CEC remote control messages to the RC framework.
+
+config CEC_PIN_ERROR_INJ
+	bool "Enable CEC error injection support"
+	depends on CEC_PIN && DEBUG_FS
+	---help---
+	  This option enables CEC error injection using debugfs.

+ 4 - 0
drivers/media/cec/Makefile

@@ -9,4 +9,8 @@ ifeq ($(CONFIG_CEC_PIN),y)
   cec-objs += cec-pin.o
 endif
 
+ifeq ($(CONFIG_CEC_PIN_ERROR_INJ),y)
+  cec-objs += cec-pin-error-inj.o
+endif
+
 obj-$(CONFIG_CEC_CORE) += cec.o

+ 23 - 31
drivers/media/cec/cec-adap.c

@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * cec-adap.c - HDMI Consumer Electronics Control framework - CEC adapter
  *
  * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #include <linux/errno.h>
@@ -85,8 +73,8 @@ static unsigned int cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr
 void cec_queue_event_fh(struct cec_fh *fh,
 			const struct cec_event *new_ev, u64 ts)
 {
-	static const u8 max_events[CEC_NUM_EVENTS] = {
-		1, 1, 64, 64, 8, 8,
+	static const u16 max_events[CEC_NUM_EVENTS] = {
+		1, 1, 800, 800, 8, 8,
 	};
 	struct cec_event_entry *entry;
 	unsigned int ev_idx = new_ev->event - 1;
@@ -154,11 +142,13 @@ static void cec_queue_event(struct cec_adapter *adap,
 }
 
 /* Notify userspace that the CEC pin changed state at the given time. */
-void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, ktime_t ts)
+void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high,
+			     bool dropped_events, ktime_t ts)
 {
 	struct cec_event ev = {
 		.event = is_high ? CEC_EVENT_PIN_CEC_HIGH :
 				   CEC_EVENT_PIN_CEC_LOW,
+		.flags = dropped_events ? CEC_EVENT_FL_DROPPED_EVENTS : 0,
 	};
 	struct cec_fh *fh;
 
@@ -711,16 +701,31 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
 	else
 		msg->flags = 0;
 
+	if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) {
+		msg->msg[2] = adap->phys_addr >> 8;
+		msg->msg[3] = adap->phys_addr & 0xff;
+	}
+
 	/* Sanity checks */
 	if (msg->len == 0 || msg->len > CEC_MAX_MSG_SIZE) {
 		dprintk(1, "%s: invalid length %d\n", __func__, msg->len);
 		return -EINVAL;
 	}
+
+	memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
+
+	if (msg->timeout)
+		dprintk(2, "%s: %*ph (wait for 0x%02x%s)\n",
+			__func__, msg->len, msg->msg, msg->reply,
+			!block ? ", nb" : "");
+	else
+		dprintk(2, "%s: %*ph%s\n",
+			__func__, msg->len, msg->msg, !block ? " (nb)" : "");
+
 	if (msg->timeout && msg->len == 1) {
-		dprintk(1, "%s: can't reply for poll msg\n", __func__);
+		dprintk(1, "%s: can't reply to poll msg\n", __func__);
 		return -EINVAL;
 	}
-	memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
 	if (msg->len == 1) {
 		if (cec_msg_destination(msg) == 0xf) {
 			dprintk(1, "%s: invalid poll message\n", __func__);
@@ -780,19 +785,6 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
 	if (!msg->sequence)
 		msg->sequence = ++adap->sequence;
 
-	if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) {
-		msg->msg[2] = adap->phys_addr >> 8;
-		msg->msg[3] = adap->phys_addr & 0xff;
-	}
-
-	if (msg->timeout)
-		dprintk(2, "%s: %*ph (wait for 0x%02x%s)\n",
-			__func__, msg->len, msg->msg, msg->reply,
-			!block ? ", nb" : "");
-	else
-		dprintk(2, "%s: %*ph%s\n",
-			__func__, msg->len, msg->msg, !block ? " (nb)" : "");
-
 	data->msg = *msg;
 	data->fh = fh;
 	data->adap = adap;

+ 1 - 13
drivers/media/cec/cec-api.c

@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * cec-api.c - HDMI Consumer Electronics Control framework - API
  *
  * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #include <linux/errno.h>

+ 59 - 13
drivers/media/cec/cec-core.c

@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * cec-core.c - HDMI Consumer Electronics Control framework - Core
  *
  * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #include <linux/errno.h>
@@ -207,6 +195,55 @@ void cec_register_cec_notifier(struct cec_adapter *adap,
 EXPORT_SYMBOL_GPL(cec_register_cec_notifier);
 #endif
 
+#ifdef CONFIG_DEBUG_FS
+static ssize_t cec_error_inj_write(struct file *file,
+	const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct seq_file *sf = file->private_data;
+	struct cec_adapter *adap = sf->private;
+	char *buf;
+	char *line;
+	char *p;
+
+	buf = memdup_user_nul(ubuf, min_t(size_t, PAGE_SIZE, count));
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+	p = buf;
+	while (p && *p) {
+		p = skip_spaces(p);
+		line = strsep(&p, "\n");
+		if (!*line || *line == '#')
+			continue;
+		if (!adap->ops->error_inj_parse_line(adap, line)) {
+			kfree(buf);
+			return -EINVAL;
+		}
+	}
+	kfree(buf);
+	return count;
+}
+
+static int cec_error_inj_show(struct seq_file *sf, void *unused)
+{
+	struct cec_adapter *adap = sf->private;
+
+	return adap->ops->error_inj_show(adap, sf);
+}
+
+static int cec_error_inj_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cec_error_inj_show, inode->i_private);
+}
+
+static const struct file_operations cec_error_inj_fops = {
+	.open = cec_error_inj_open,
+	.write = cec_error_inj_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+#endif
+
 struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
 					 void *priv, const char *name, u32 caps,
 					 u8 available_las)
@@ -346,7 +383,16 @@ int cec_register_adapter(struct cec_adapter *adap,
 		pr_warn("cec-%s: Failed to create status file\n", adap->name);
 		debugfs_remove_recursive(adap->cec_dir);
 		adap->cec_dir = NULL;
+		return 0;
 	}
+	if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line)
+		return 0;
+	adap->error_inj_file = debugfs_create_file("error-inj", 0644,
+						   adap->cec_dir, adap,
+						   &cec_error_inj_fops);
+	if (IS_ERR_OR_NULL(adap->error_inj_file))
+		pr_warn("cec-%s: Failed to create error-inj file\n",
+			adap->name);
 #endif
 	return 0;
 }

+ 1 - 13
drivers/media/cec/cec-edid.c

@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions
  *
  * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #include <linux/module.h>

+ 1 - 13
drivers/media/cec/cec-notifier.c

@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * cec-notifier.c - notify CEC drivers of physical address changes
  *
  * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk>
  * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #include <linux/export.h>

+ 342 - 0
drivers/media/cec/cec-pin-error-inj.c

@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched/types.h>
+
+#include <media/cec-pin.h>
+#include "cec-pin-priv.h"
+
+struct cec_error_inj_cmd {
+	unsigned int mode_offset;
+	int arg_idx;
+	const char *cmd;
+};
+
+static const struct cec_error_inj_cmd cec_error_inj_cmds[] = {
+	{ CEC_ERROR_INJ_RX_NACK_OFFSET, -1, "rx-nack" },
+	{ CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET,
+	  CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, "rx-low-drive" },
+	{ CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, "rx-add-byte" },
+	{ CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, "rx-remove-byte" },
+	{ CEC_ERROR_INJ_RX_ARB_LOST_OFFSET,
+	  CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, "rx-arb-lost" },
+
+	{ CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, "tx-no-eom" },
+	{ CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, "tx-early-eom" },
+	{ CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET,
+	  CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, "tx-add-bytes" },
+	{ CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, "tx-remove-byte" },
+	{ CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET,
+	  CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, "tx-short-bit" },
+	{ CEC_ERROR_INJ_TX_LONG_BIT_OFFSET,
+	  CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, "tx-long-bit" },
+	{ CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET,
+	  CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, "tx-custom-bit" },
+	{ CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, "tx-short-start" },
+	{ CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, "tx-long-start" },
+	{ CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET, -1, "tx-custom-start" },
+	{ CEC_ERROR_INJ_TX_LAST_BIT_OFFSET,
+	  CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, "tx-last-bit" },
+	{ CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET,
+	  CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, "tx-low-drive" },
+	{ 0, -1, NULL }
+};
+
+u16 cec_pin_rx_error_inj(struct cec_pin *pin)
+{
+	u16 cmd = CEC_ERROR_INJ_OP_ANY;
+
+	/* Only when 18 bits have been received do we have a valid cmd */
+	if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) &&
+	    pin->rx_bit >= 18)
+		cmd = pin->rx_msg.msg[1];
+	return (pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) ? cmd :
+		CEC_ERROR_INJ_OP_ANY;
+}
+
+u16 cec_pin_tx_error_inj(struct cec_pin *pin)
+{
+	u16 cmd = CEC_ERROR_INJ_OP_ANY;
+
+	if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) &&
+	    pin->tx_msg.len > 1)
+		cmd = pin->tx_msg.msg[1];
+	return (pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) ? cmd :
+		CEC_ERROR_INJ_OP_ANY;
+}
+
+bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
+{
+	static const char *delims = " \t\r";
+	struct cec_pin *pin = adap->pin;
+	unsigned int i;
+	bool has_pos = false;
+	char *p = line;
+	char *token;
+	char *comma;
+	u64 *error;
+	u8 *args;
+	bool has_op;
+	u32 op;
+	u8 mode;
+	u8 pos;
+	u8 v;
+
+	p = skip_spaces(p);
+	token = strsep(&p, delims);
+	if (!strcmp(token, "clear")) {
+		memset(pin->error_inj, 0, sizeof(pin->error_inj));
+		pin->rx_toggle = pin->tx_toggle = false;
+		pin->tx_ignore_nack_until_eom = false;
+		pin->tx_custom_pulse = false;
+		pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
+		pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+		return true;
+	}
+	if (!strcmp(token, "rx-clear")) {
+		for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
+			pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK;
+		pin->rx_toggle = false;
+		return true;
+	}
+	if (!strcmp(token, "tx-clear")) {
+		for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
+			pin->error_inj[i] &= ~CEC_ERROR_INJ_TX_MASK;
+		pin->tx_toggle = false;
+		pin->tx_ignore_nack_until_eom = false;
+		pin->tx_custom_pulse = false;
+		pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
+		pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+		return true;
+	}
+	if (!strcmp(token, "tx-ignore-nack-until-eom")) {
+		pin->tx_ignore_nack_until_eom = true;
+		return true;
+	}
+	if (!strcmp(token, "tx-custom-pulse")) {
+		pin->tx_custom_pulse = true;
+		cec_pin_start_timer(pin);
+		return true;
+	}
+	if (!p)
+		return false;
+
+	p = skip_spaces(p);
+	if (!strcmp(token, "tx-custom-low-usecs")) {
+		u32 usecs;
+
+		if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
+			return false;
+		pin->tx_custom_low_usecs = usecs;
+		return true;
+	}
+	if (!strcmp(token, "tx-custom-high-usecs")) {
+		u32 usecs;
+
+		if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
+			return false;
+		pin->tx_custom_high_usecs = usecs;
+		return true;
+	}
+
+	comma = strchr(token, ',');
+	if (comma)
+		*comma++ = '\0';
+	if (!strcmp(token, "any"))
+		op = CEC_ERROR_INJ_OP_ANY;
+	else if (!kstrtou8(token, 0, &v))
+		op = v;
+	else
+		return false;
+	mode = CEC_ERROR_INJ_MODE_ONCE;
+	if (comma) {
+		if (!strcmp(comma, "off"))
+			mode = CEC_ERROR_INJ_MODE_OFF;
+		else if (!strcmp(comma, "once"))
+			mode = CEC_ERROR_INJ_MODE_ONCE;
+		else if (!strcmp(comma, "always"))
+			mode = CEC_ERROR_INJ_MODE_ALWAYS;
+		else if (!strcmp(comma, "toggle"))
+			mode = CEC_ERROR_INJ_MODE_TOGGLE;
+		else
+			return false;
+	}
+
+	error = pin->error_inj + op;
+	args = pin->error_inj_args[op];
+	has_op = op <= 0xff;
+
+	token = strsep(&p, delims);
+	if (p) {
+		p = skip_spaces(p);
+		has_pos = !kstrtou8(p, 0, &pos);
+	}
+
+	if (!strcmp(token, "clear")) {
+		*error = 0;
+		return true;
+	}
+	if (!strcmp(token, "rx-clear")) {
+		*error &= ~CEC_ERROR_INJ_RX_MASK;
+		return true;
+	}
+	if (!strcmp(token, "tx-clear")) {
+		*error &= ~CEC_ERROR_INJ_TX_MASK;
+		return true;
+	}
+
+	for (i = 0; cec_error_inj_cmds[i].cmd; i++) {
+		const char *cmd = cec_error_inj_cmds[i].cmd;
+		unsigned int mode_offset;
+		u64 mode_mask;
+		int arg_idx;
+		bool is_bit_pos = true;
+
+		if (strcmp(token, cmd))
+			continue;
+
+		mode_offset = cec_error_inj_cmds[i].mode_offset;
+		mode_mask = CEC_ERROR_INJ_MODE_MASK << mode_offset;
+		arg_idx = cec_error_inj_cmds[i].arg_idx;
+
+		if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET ||
+		    mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET)
+			is_bit_pos = false;
+
+		if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) {
+			if (has_op)
+				return false;
+			if (!has_pos)
+				pos = 0x0f;
+		}
+		if (arg_idx >= 0 && is_bit_pos) {
+			if (!has_pos || pos >= 160)
+				return false;
+			if (has_op && pos < 10 + 8)
+				return false;
+			/* Invalid bit position may not be the Ack bit */
+			if ((mode_offset == CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET ||
+			     mode_offset == CEC_ERROR_INJ_TX_LONG_BIT_OFFSET ||
+			     mode_offset == CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET) &&
+			    (pos % 10) == 9)
+				return false;
+		}
+		*error &= ~mode_mask;
+		*error |= (u64)mode << mode_offset;
+		if (arg_idx >= 0)
+			args[arg_idx] = pos;
+		return true;
+	}
+	return false;
+}
+
+static void cec_pin_show_cmd(struct seq_file *sf, u32 cmd, u8 mode)
+{
+	if (cmd == CEC_ERROR_INJ_OP_ANY)
+		seq_puts(sf, "any,");
+	else
+		seq_printf(sf, "0x%02x,", cmd);
+	switch (mode) {
+	case CEC_ERROR_INJ_MODE_ONCE:
+		seq_puts(sf, "once ");
+		break;
+	case CEC_ERROR_INJ_MODE_ALWAYS:
+		seq_puts(sf, "always ");
+		break;
+	case CEC_ERROR_INJ_MODE_TOGGLE:
+		seq_puts(sf, "toggle ");
+		break;
+	default:
+		seq_puts(sf, "off ");
+		break;
+	}
+}
+
+int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
+{
+	struct cec_pin *pin = adap->pin;
+	unsigned int i, j;
+
+	seq_puts(sf, "# Clear error injections:\n");
+	seq_puts(sf, "#   clear          clear all rx and tx error injections\n");
+	seq_puts(sf, "#   rx-clear       clear all rx error injections\n");
+	seq_puts(sf, "#   tx-clear       clear all tx error injections\n");
+	seq_puts(sf, "#   <op> clear     clear all rx and tx error injections for <op>\n");
+	seq_puts(sf, "#   <op> rx-clear  clear all rx error injections for <op>\n");
+	seq_puts(sf, "#   <op> tx-clear  clear all tx error injections for <op>\n");
+	seq_puts(sf, "#\n");
+	seq_puts(sf, "# RX error injection:\n");
+	seq_puts(sf, "#   <op>[,<mode>] rx-nack              NACK the message instead of sending an ACK\n");
+	seq_puts(sf, "#   <op>[,<mode>] rx-low-drive <bit>   force a low-drive condition at this bit position\n");
+	seq_puts(sf, "#   <op>[,<mode>] rx-add-byte          add a spurious byte to the received CEC message\n");
+	seq_puts(sf, "#   <op>[,<mode>] rx-remove-byte       remove the last byte from the received CEC message\n");
+	seq_puts(sf, "#   <op>[,<mode>] rx-arb-lost <poll>   generate a POLL message to trigger an arbitration lost\n");
+	seq_puts(sf, "#\n");
+	seq_puts(sf, "# TX error injection settings:\n");
+	seq_puts(sf, "#   tx-ignore-nack-until-eom           ignore early NACKs until EOM\n");
+	seq_puts(sf, "#   tx-custom-low-usecs <usecs>        define the 'low' time for the custom pulse\n");
+	seq_puts(sf, "#   tx-custom-high-usecs <usecs>       define the 'high' time for the custom pulse\n");
+	seq_puts(sf, "#   tx-custom-pulse                    transmit the custom pulse once the bus is idle\n");
+	seq_puts(sf, "#\n");
+	seq_puts(sf, "# TX error injection:\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-no-eom            don't set the EOM bit\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-early-eom         set the EOM bit one byte too soon\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-add-bytes <num>   append <num> (1-255) spurious bytes to the message\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-remove-byte       drop the last byte from the message\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-short-bit <bit>   make this bit shorter than allowed\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-long-bit <bit>    make this bit longer than allowed\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-custom-bit <bit>  send the custom pulse instead of this bit\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-short-start       send a start pulse that's too short\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-long-start        send a start pulse that's too long\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-custom-start      send the custom pulse instead of the start pulse\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-last-bit <bit>    stop sending after this bit\n");
+	seq_puts(sf, "#   <op>[,<mode>] tx-low-drive <bit>   force a low-drive condition at this bit position\n");
+	seq_puts(sf, "#\n");
+	seq_puts(sf, "# <op>       CEC message opcode (0-255) or 'any'\n");
+	seq_puts(sf, "# <mode>     'once' (default), 'always', 'toggle' or 'off'\n");
+	seq_puts(sf, "# <bit>      CEC message bit (0-159)\n");
+	seq_puts(sf, "#            10 bits per 'byte': bits 0-7: data, bit 8: EOM, bit 9: ACK\n");
+	seq_puts(sf, "# <poll>     CEC poll message used to test arbitration lost (0x00-0xff, default 0x0f)\n");
+	seq_puts(sf, "# <usecs>    microseconds (0-10000000, default 1000)\n");
+
+	seq_puts(sf, "\nclear\n");
+
+	for (i = 0; i < ARRAY_SIZE(pin->error_inj); i++) {
+		u64 e = pin->error_inj[i];
+
+		for (j = 0; cec_error_inj_cmds[j].cmd; j++) {
+			const char *cmd = cec_error_inj_cmds[j].cmd;
+			unsigned int mode;
+			unsigned int mode_offset;
+			int arg_idx;
+
+			mode_offset = cec_error_inj_cmds[j].mode_offset;
+			arg_idx = cec_error_inj_cmds[j].arg_idx;
+			mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
+			if (!mode)
+				continue;
+			cec_pin_show_cmd(sf, i, mode);
+			seq_puts(sf, cmd);
+			if (arg_idx >= 0)
+				seq_printf(sf, " %u",
+					   pin->error_inj_args[i][arg_idx]);
+			seq_puts(sf, "\n");
+		}
+	}
+
+	if (pin->tx_ignore_nack_until_eom)
+		seq_puts(sf, "tx-ignore-nack-until-eom\n");
+	if (pin->tx_custom_pulse)
+		seq_puts(sf, "tx-custom-pulse\n");
+	if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT)
+		seq_printf(sf, "tx-custom-low-usecs %u\n",
+			   pin->tx_custom_low_usecs);
+	if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT)
+		seq_printf(sf, "tx-custom-high-usecs %u\n",
+			   pin->tx_custom_high_usecs);
+	return 0;
+}

+ 128 - 20
drivers/media/cec/cec-pin-priv.h

@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * cec-pin-priv.h - internal cec-pin header
  *
  * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #ifndef LINUX_CEC_PIN_PRIV_H
@@ -40,14 +28,30 @@ enum cec_pin_state {
 	CEC_ST_TX_START_BIT_LOW,
 	/* Drive CEC high for the start bit */
 	CEC_ST_TX_START_BIT_HIGH,
+	/* Generate a start bit period that is too short */
+	CEC_ST_TX_START_BIT_HIGH_SHORT,
+	/* Generate a start bit period that is too long */
+	CEC_ST_TX_START_BIT_HIGH_LONG,
+	/* Drive CEC low for the start bit using the custom timing */
+	CEC_ST_TX_START_BIT_LOW_CUSTOM,
+	/* Drive CEC high for the start bit using the custom timing */
+	CEC_ST_TX_START_BIT_HIGH_CUSTOM,
 	/* Drive CEC low for the 0 bit */
 	CEC_ST_TX_DATA_BIT_0_LOW,
 	/* Drive CEC high for the 0 bit */
 	CEC_ST_TX_DATA_BIT_0_HIGH,
+	/* Generate a bit period that is too short */
+	CEC_ST_TX_DATA_BIT_0_HIGH_SHORT,
+	/* Generate a bit period that is too long */
+	CEC_ST_TX_DATA_BIT_0_HIGH_LONG,
 	/* Drive CEC low for the 1 bit */
 	CEC_ST_TX_DATA_BIT_1_LOW,
 	/* Drive CEC high for the 1 bit */
 	CEC_ST_TX_DATA_BIT_1_HIGH,
+	/* Generate a bit period that is too short */
+	CEC_ST_TX_DATA_BIT_1_HIGH_SHORT,
+	/* Generate a bit period that is too long */
+	CEC_ST_TX_DATA_BIT_1_HIGH_LONG,
 	/*
 	 * Wait for start of sample time to check for Ack bit or first
 	 * four initiator bits to check for Arbitration Lost.
@@ -55,6 +59,20 @@ enum cec_pin_state {
 	CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE,
 	/* Wait for end of bit period after sampling */
 	CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE,
+	/* Generate a bit period that is too short */
+	CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT,
+	/* Generate a bit period that is too long */
+	CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG,
+	/* Drive CEC low for a data bit using the custom timing */
+	CEC_ST_TX_DATA_BIT_LOW_CUSTOM,
+	/* Drive CEC high for a data bit using the custom timing */
+	CEC_ST_TX_DATA_BIT_HIGH_CUSTOM,
+	/* Drive CEC low for a standalone pulse using the custom timing */
+	CEC_ST_TX_PULSE_LOW_CUSTOM,
+	/* Drive CEC high for a standalone pulse using the custom timing */
+	CEC_ST_TX_PULSE_HIGH_CUSTOM,
+	/* Start low drive */
+	CEC_ST_TX_LOW_DRIVE,
 
 	/* Rx states */
 
@@ -66,8 +84,8 @@ enum cec_pin_state {
 	CEC_ST_RX_DATA_SAMPLE,
 	/* Wait for earliest end of bit period after sampling */
 	CEC_ST_RX_DATA_POST_SAMPLE,
-	/* Wait for CEC to go high (i.e. end of bit period */
-	CEC_ST_RX_DATA_HIGH,
+	/* Wait for CEC to go low (i.e. end of bit period) */
+	CEC_ST_RX_DATA_WAIT_FOR_LOW,
 	/* Drive CEC low to send 0 Ack bit */
 	CEC_ST_RX_ACK_LOW,
 	/* End of 0 Ack time, wait for earliest end of bit period */
@@ -76,9 +94,9 @@ enum cec_pin_state {
 	CEC_ST_RX_ACK_HIGH_POST,
 	/* Wait for earliest end of bit period and end of message */
 	CEC_ST_RX_ACK_FINISH,
-
 	/* Start low drive */
-	CEC_ST_LOW_DRIVE,
+	CEC_ST_RX_LOW_DRIVE,
+
 	/* Monitor pin using interrupts */
 	CEC_ST_RX_IRQ,
 
@@ -86,7 +104,58 @@ enum cec_pin_state {
 	CEC_PIN_STATES
 };
 
-#define CEC_NUM_PIN_EVENTS 128
+/* Error Injection */
+
+/* Error injection modes */
+#define CEC_ERROR_INJ_MODE_OFF				0
+#define CEC_ERROR_INJ_MODE_ONCE				1
+#define CEC_ERROR_INJ_MODE_ALWAYS			2
+#define CEC_ERROR_INJ_MODE_TOGGLE			3
+#define CEC_ERROR_INJ_MODE_MASK				3ULL
+
+/* Receive error injection options */
+#define CEC_ERROR_INJ_RX_NACK_OFFSET			0
+#define CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET		2
+#define CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET		4
+#define CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET		6
+#define CEC_ERROR_INJ_RX_ARB_LOST_OFFSET		8
+#define CEC_ERROR_INJ_RX_MASK				0xffffULL
+
+/* Transmit error injection options */
+#define CEC_ERROR_INJ_TX_NO_EOM_OFFSET			16
+#define CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET		18
+#define CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET		20
+#define CEC_ERROR_INJ_TX_LONG_BIT_OFFSET		22
+#define CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET		24
+#define CEC_ERROR_INJ_TX_SHORT_START_OFFSET		26
+#define CEC_ERROR_INJ_TX_LONG_START_OFFSET		28
+#define CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET		30
+#define CEC_ERROR_INJ_TX_LAST_BIT_OFFSET		32
+#define CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET		34
+#define CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET		36
+#define CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET		38
+#define CEC_ERROR_INJ_TX_MASK				0xffffffffffff0000ULL
+
+#define CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX		0
+#define CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX		1
+
+#define CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX		2
+#define CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX		3
+#define CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX		4
+#define CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX		5
+#define CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX		6
+#define CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX		7
+#define CEC_ERROR_INJ_NUM_ARGS				8
+
+/* Special CEC op values */
+#define CEC_ERROR_INJ_OP_ANY				0x00000100
+
+/* The default for the low/high time of the custom pulse */
+#define CEC_TIM_CUSTOM_DEFAULT				1000
+
+#define CEC_NUM_PIN_EVENTS				128
+#define CEC_PIN_EVENT_FL_IS_HIGH			(1 << 0)
+#define CEC_PIN_EVENT_FL_DROPPED			(1 << 1)
 
 #define CEC_PIN_IRQ_UNCHANGED	0
 #define CEC_PIN_IRQ_DISABLE	1
@@ -110,24 +179,63 @@ struct cec_pin {
 	u32				tx_bit;
 	bool				tx_nacked;
 	u32				tx_signal_free_time;
+	bool				tx_toggle;
 	struct cec_msg			rx_msg;
 	u32				rx_bit;
+	bool				rx_toggle;
+	u32				rx_start_bit_low_too_short_cnt;
+	u64				rx_start_bit_low_too_short_ts;
+	u32				rx_start_bit_low_too_short_delta;
+	u32				rx_start_bit_too_short_cnt;
+	u64				rx_start_bit_too_short_ts;
+	u32				rx_start_bit_too_short_delta;
+	u32				rx_start_bit_too_long_cnt;
+	u32				rx_data_bit_too_short_cnt;
+	u64				rx_data_bit_too_short_ts;
+	u32				rx_data_bit_too_short_delta;
+	u32				rx_data_bit_too_long_cnt;
+	u32				rx_low_drive_cnt;
 
 	struct cec_msg			work_rx_msg;
 	u8				work_tx_status;
 	ktime_t				work_tx_ts;
 	atomic_t			work_irq_change;
-	atomic_t			work_pin_events;
+	atomic_t			work_pin_num_events;
 	unsigned int			work_pin_events_wr;
 	unsigned int			work_pin_events_rd;
 	ktime_t				work_pin_ts[CEC_NUM_PIN_EVENTS];
-	bool				work_pin_is_high[CEC_NUM_PIN_EVENTS];
+	u8				work_pin_events[CEC_NUM_PIN_EVENTS];
+	bool				work_pin_events_dropped;
+	u32				work_pin_events_dropped_cnt;
 	ktime_t				timer_ts;
 	u32				timer_cnt;
 	u32				timer_100ms_overruns;
 	u32				timer_300ms_overruns;
 	u32				timer_max_overrun;
 	u32				timer_sum_overrun;
+
+	u32				tx_custom_low_usecs;
+	u32				tx_custom_high_usecs;
+	bool				tx_ignore_nack_until_eom;
+	bool				tx_custom_pulse;
+	bool				tx_generated_poll;
+	bool				tx_post_eom;
+	u8				tx_extra_bytes;
+	u32				tx_low_drive_cnt;
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+	u64				error_inj[CEC_ERROR_INJ_OP_ANY + 1];
+	u8				error_inj_args[CEC_ERROR_INJ_OP_ANY + 1][CEC_ERROR_INJ_NUM_ARGS];
+#endif
 };
 
+void cec_pin_start_timer(struct cec_pin *pin);
+
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line);
+int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf);
+
+u16 cec_pin_rx_error_inj(struct cec_pin *pin);
+u16 cec_pin_tx_error_inj(struct cec_pin *pin);
+#endif
+
 #endif

+ 591 - 87
drivers/media/cec/cec-pin.c

@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #include <linux/delay.h>
@@ -51,11 +39,29 @@
 #define CEC_TIM_IDLE_SAMPLE		1000
 /* when processing the start bit, sample twice per millisecond */
 #define CEC_TIM_START_BIT_SAMPLE	500
-/* when polling for a state change, sample once every 50 micoseconds */
+/* when polling for a state change, sample once every 50 microseconds */
 #define CEC_TIM_SAMPLE			50
 
 #define CEC_TIM_LOW_DRIVE_ERROR		(1.5 * CEC_TIM_DATA_BIT_TOTAL)
 
+/*
+ * Total data bit time that is too short/long for a valid bit,
+ * used for error injection.
+ */
+#define CEC_TIM_DATA_BIT_TOTAL_SHORT	1800
+#define CEC_TIM_DATA_BIT_TOTAL_LONG	2900
+
+/*
+ * Total start bit time that is too short/long for a valid bit,
+ * used for error injection.
+ */
+#define CEC_TIM_START_BIT_TOTAL_SHORT	4100
+#define CEC_TIM_START_BIT_TOTAL_LONG	5000
+
+/* Data bits are 0-7, EOM is bit 8 and ACK is bit 9 */
+#define EOM_BIT				8
+#define ACK_BIT				9
+
 struct cec_state {
 	const char * const name;
 	unsigned int usecs;
@@ -68,17 +74,32 @@ static const struct cec_state states[CEC_PIN_STATES] = {
 	{ "Tx Wait for High",	   CEC_TIM_IDLE_SAMPLE },
 	{ "Tx Start Bit Low",	   CEC_TIM_START_BIT_LOW },
 	{ "Tx Start Bit High",	   CEC_TIM_START_BIT_TOTAL - CEC_TIM_START_BIT_LOW },
+	{ "Tx Start Bit High Short", CEC_TIM_START_BIT_TOTAL_SHORT - CEC_TIM_START_BIT_LOW },
+	{ "Tx Start Bit High Long", CEC_TIM_START_BIT_TOTAL_LONG - CEC_TIM_START_BIT_LOW },
+	{ "Tx Start Bit Low Custom", 0 },
+	{ "Tx Start Bit High Custom", 0 },
 	{ "Tx Data 0 Low",	   CEC_TIM_DATA_BIT_0_LOW },
 	{ "Tx Data 0 High",	   CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_0_LOW },
+	{ "Tx Data 0 High Short",  CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_0_LOW },
+	{ "Tx Data 0 High Long",   CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_0_LOW },
 	{ "Tx Data 1 Low",	   CEC_TIM_DATA_BIT_1_LOW },
 	{ "Tx Data 1 High",	   CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_1_LOW },
-	{ "Tx Data 1 Pre Sample",  CEC_TIM_DATA_BIT_SAMPLE - CEC_TIM_DATA_BIT_1_LOW },
-	{ "Tx Data 1 Post Sample", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_SAMPLE },
+	{ "Tx Data 1 High Short",  CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_1_LOW },
+	{ "Tx Data 1 High Long",   CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_1_LOW },
+	{ "Tx Data 1 High Pre Sample", CEC_TIM_DATA_BIT_SAMPLE - CEC_TIM_DATA_BIT_1_LOW },
+	{ "Tx Data 1 High Post Sample", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_SAMPLE },
+	{ "Tx Data 1 High Post Sample Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_SAMPLE },
+	{ "Tx Data 1 High Post Sample Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_SAMPLE },
+	{ "Tx Data Bit Low Custom", 0 },
+	{ "Tx Data Bit High Custom", 0 },
+	{ "Tx Pulse Low Custom",   0 },
+	{ "Tx Pulse High Custom",  0 },
+	{ "Tx Low Drive",	   CEC_TIM_LOW_DRIVE_ERROR },
 	{ "Rx Start Bit Low",	   CEC_TIM_SAMPLE },
 	{ "Rx Start Bit High",	   CEC_TIM_SAMPLE },
 	{ "Rx Data Sample",	   CEC_TIM_DATA_BIT_SAMPLE },
 	{ "Rx Data Post Sample",   CEC_TIM_DATA_BIT_HIGH - CEC_TIM_DATA_BIT_SAMPLE },
-	{ "Rx Data High",	   CEC_TIM_SAMPLE },
+	{ "Rx Data Wait for Low",  CEC_TIM_SAMPLE },
 	{ "Rx Ack Low",		   CEC_TIM_DATA_BIT_0_LOW },
 	{ "Rx Ack Low Post",	   CEC_TIM_DATA_BIT_HIGH - CEC_TIM_DATA_BIT_0_LOW },
 	{ "Rx Ack High Post",	   CEC_TIM_DATA_BIT_HIGH },
@@ -93,12 +114,21 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force)
 		return;
 
 	pin->adap->cec_pin_is_high = v;
-	if (atomic_read(&pin->work_pin_events) < CEC_NUM_PIN_EVENTS) {
-		pin->work_pin_is_high[pin->work_pin_events_wr] = v;
+	if (atomic_read(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) {
+		u8 ev = v;
+
+		if (pin->work_pin_events_dropped) {
+			pin->work_pin_events_dropped = false;
+			v |= CEC_PIN_EVENT_FL_DROPPED;
+		}
+		pin->work_pin_events[pin->work_pin_events_wr] = ev;
 		pin->work_pin_ts[pin->work_pin_events_wr] = ktime_get();
 		pin->work_pin_events_wr =
 			(pin->work_pin_events_wr + 1) % CEC_NUM_PIN_EVENTS;
-		atomic_inc(&pin->work_pin_events);
+		atomic_inc(&pin->work_pin_num_events);
+	} else {
+		pin->work_pin_events_dropped = true;
+		pin->work_pin_events_dropped_cnt++;
 	}
 	wake_up_interruptible(&pin->kthread_waitq);
 }
@@ -123,6 +153,173 @@ static bool cec_pin_high(struct cec_pin *pin)
 	return cec_pin_read(pin);
 }
 
+static bool rx_error_inj(struct cec_pin *pin, unsigned int mode_offset,
+			 int arg_idx, u8 *arg)
+{
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+	u16 cmd = cec_pin_rx_error_inj(pin);
+	u64 e = pin->error_inj[cmd];
+	unsigned int mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
+
+	if (arg_idx >= 0) {
+		u8 pos = pin->error_inj_args[cmd][arg_idx];
+
+		if (arg)
+			*arg = pos;
+		else if (pos != pin->rx_bit)
+			return false;
+	}
+
+	switch (mode) {
+	case CEC_ERROR_INJ_MODE_ONCE:
+		pin->error_inj[cmd] &=
+			~(CEC_ERROR_INJ_MODE_MASK << mode_offset);
+		return true;
+	case CEC_ERROR_INJ_MODE_ALWAYS:
+		return true;
+	case CEC_ERROR_INJ_MODE_TOGGLE:
+		return pin->rx_toggle;
+	default:
+		return false;
+	}
+#else
+	return false;
+#endif
+}
+
+static bool rx_nack(struct cec_pin *pin)
+{
+	return rx_error_inj(pin, CEC_ERROR_INJ_RX_NACK_OFFSET, -1, NULL);
+}
+
+static bool rx_low_drive(struct cec_pin *pin)
+{
+	return rx_error_inj(pin, CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET,
+			    CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, NULL);
+}
+
+static bool rx_add_byte(struct cec_pin *pin)
+{
+	return rx_error_inj(pin, CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, NULL);
+}
+
+static bool rx_remove_byte(struct cec_pin *pin)
+{
+	return rx_error_inj(pin, CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, NULL);
+}
+
+static bool rx_arb_lost(struct cec_pin *pin, u8 *poll)
+{
+	return pin->tx_msg.len == 0 &&
+		rx_error_inj(pin, CEC_ERROR_INJ_RX_ARB_LOST_OFFSET,
+			     CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, poll);
+}
+
+static bool tx_error_inj(struct cec_pin *pin, unsigned int mode_offset,
+			 int arg_idx, u8 *arg)
+{
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+	u16 cmd = cec_pin_tx_error_inj(pin);
+	u64 e = pin->error_inj[cmd];
+	unsigned int mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
+
+	if (arg_idx >= 0) {
+		u8 pos = pin->error_inj_args[cmd][arg_idx];
+
+		if (arg)
+			*arg = pos;
+		else if (pos != pin->tx_bit)
+			return false;
+	}
+
+	switch (mode) {
+	case CEC_ERROR_INJ_MODE_ONCE:
+		pin->error_inj[cmd] &=
+			~(CEC_ERROR_INJ_MODE_MASK << mode_offset);
+		return true;
+	case CEC_ERROR_INJ_MODE_ALWAYS:
+		return true;
+	case CEC_ERROR_INJ_MODE_TOGGLE:
+		return pin->tx_toggle;
+	default:
+		return false;
+	}
+#else
+	return false;
+#endif
+}
+
+static bool tx_no_eom(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, NULL);
+}
+
+static bool tx_early_eom(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, NULL);
+}
+
+static bool tx_short_bit(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET,
+			    CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, NULL);
+}
+
+static bool tx_long_bit(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_LONG_BIT_OFFSET,
+			    CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, NULL);
+}
+
+static bool tx_custom_bit(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET,
+			    CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, NULL);
+}
+
+static bool tx_short_start(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, NULL);
+}
+
+static bool tx_long_start(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, NULL);
+}
+
+static bool tx_custom_start(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET,
+			    -1, NULL);
+}
+
+static bool tx_last_bit(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_LAST_BIT_OFFSET,
+			    CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, NULL);
+}
+
+static u8 tx_add_bytes(struct cec_pin *pin)
+{
+	u8 bytes;
+
+	if (tx_error_inj(pin, CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET,
+			 CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, &bytes))
+		return bytes;
+	return 0;
+}
+
+static bool tx_remove_byte(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, NULL);
+}
+
+static bool tx_low_drive(struct cec_pin *pin)
+{
+	return tx_error_inj(pin, CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET,
+			    CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, NULL);
+}
+
 static void cec_pin_to_idle(struct cec_pin *pin)
 {
 	/*
@@ -132,8 +329,16 @@ static void cec_pin_to_idle(struct cec_pin *pin)
 	pin->rx_bit = pin->tx_bit = 0;
 	pin->rx_msg.len = 0;
 	memset(pin->rx_msg.msg, 0, sizeof(pin->rx_msg.msg));
-	pin->state = CEC_ST_IDLE;
 	pin->ts = ns_to_ktime(0);
+	pin->tx_generated_poll = false;
+	pin->tx_post_eom = false;
+	if (pin->state >= CEC_ST_TX_WAIT &&
+	    pin->state <= CEC_ST_TX_LOW_DRIVE)
+		pin->tx_toggle ^= 1;
+	if (pin->state >= CEC_ST_RX_START_BIT_LOW &&
+	    pin->state <= CEC_ST_RX_LOW_DRIVE)
+		pin->rx_toggle ^= 1;
+	pin->state = CEC_ST_IDLE;
 }
 
 /*
@@ -174,42 +379,109 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
 		break;
 
 	case CEC_ST_TX_START_BIT_LOW:
-		pin->state = CEC_ST_TX_START_BIT_HIGH;
+		if (tx_short_start(pin)) {
+			/*
+			 * Error Injection: send an invalid (too short)
+			 * start pulse.
+			 */
+			pin->state = CEC_ST_TX_START_BIT_HIGH_SHORT;
+		} else if (tx_long_start(pin)) {
+			/*
+			 * Error Injection: send an invalid (too long)
+			 * start pulse.
+			 */
+			pin->state = CEC_ST_TX_START_BIT_HIGH_LONG;
+		} else {
+			pin->state = CEC_ST_TX_START_BIT_HIGH;
+		}
+		/* Generate start bit */
+		cec_pin_high(pin);
+		break;
+
+	case CEC_ST_TX_START_BIT_LOW_CUSTOM:
+		pin->state = CEC_ST_TX_START_BIT_HIGH_CUSTOM;
 		/* Generate start bit */
 		cec_pin_high(pin);
 		break;
 
 	case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE:
-		/* If the read value is 1, then all is OK */
-		if (!cec_pin_read(pin)) {
+	case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT:
+	case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG:
+		if (pin->tx_nacked) {
+			cec_pin_to_idle(pin);
+			pin->tx_msg.len = 0;
+			if (pin->tx_generated_poll)
+				break;
+			pin->work_tx_ts = ts;
+			pin->work_tx_status = CEC_TX_STATUS_NACK;
+			wake_up_interruptible(&pin->kthread_waitq);
+			break;
+		}
+		/* fall through */
+	case CEC_ST_TX_DATA_BIT_0_HIGH:
+	case CEC_ST_TX_DATA_BIT_0_HIGH_SHORT:
+	case CEC_ST_TX_DATA_BIT_0_HIGH_LONG:
+	case CEC_ST_TX_DATA_BIT_1_HIGH:
+	case CEC_ST_TX_DATA_BIT_1_HIGH_SHORT:
+	case CEC_ST_TX_DATA_BIT_1_HIGH_LONG:
+		/*
+		 * If the read value is 1, then all is OK, otherwise we have a
+		 * low drive condition.
+		 *
+		 * Special case: when we generate a poll message due to an
+		 * Arbitration Lost error injection, then ignore this since
+		 * the pin can actually be low in that case.
+		 */
+		if (!cec_pin_read(pin) && !pin->tx_generated_poll) {
 			/*
 			 * It's 0, so someone detected an error and pulled the
 			 * line low for 1.5 times the nominal bit period.
 			 */
 			pin->tx_msg.len = 0;
+			pin->state = CEC_ST_TX_WAIT_FOR_HIGH;
 			pin->work_tx_ts = ts;
 			pin->work_tx_status = CEC_TX_STATUS_LOW_DRIVE;
-			pin->state = CEC_ST_TX_WAIT_FOR_HIGH;
+			pin->tx_low_drive_cnt++;
 			wake_up_interruptible(&pin->kthread_waitq);
 			break;
 		}
-		if (pin->tx_nacked) {
+		/* fall through */
+	case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM:
+		if (tx_last_bit(pin)) {
+			/* Error Injection: just stop sending after this bit */
 			cec_pin_to_idle(pin);
 			pin->tx_msg.len = 0;
+			if (pin->tx_generated_poll)
+				break;
 			pin->work_tx_ts = ts;
-			pin->work_tx_status = CEC_TX_STATUS_NACK;
+			pin->work_tx_status = CEC_TX_STATUS_OK;
 			wake_up_interruptible(&pin->kthread_waitq);
 			break;
 		}
-		/* fall through */
-	case CEC_ST_TX_DATA_BIT_0_HIGH:
-	case CEC_ST_TX_DATA_BIT_1_HIGH:
 		pin->tx_bit++;
 		/* fall through */
 	case CEC_ST_TX_START_BIT_HIGH:
-		if (pin->tx_bit / 10 >= pin->tx_msg.len) {
+	case CEC_ST_TX_START_BIT_HIGH_SHORT:
+	case CEC_ST_TX_START_BIT_HIGH_LONG:
+	case CEC_ST_TX_START_BIT_HIGH_CUSTOM:
+		if (tx_low_drive(pin)) {
+			/* Error injection: go to low drive */
+			cec_pin_low(pin);
+			pin->state = CEC_ST_TX_LOW_DRIVE;
+			pin->tx_msg.len = 0;
+			if (pin->tx_generated_poll)
+				break;
+			pin->work_tx_ts = ts;
+			pin->work_tx_status = CEC_TX_STATUS_LOW_DRIVE;
+			pin->tx_low_drive_cnt++;
+			wake_up_interruptible(&pin->kthread_waitq);
+			break;
+		}
+		if (pin->tx_bit / 10 >= pin->tx_msg.len + pin->tx_extra_bytes) {
 			cec_pin_to_idle(pin);
 			pin->tx_msg.len = 0;
+			if (pin->tx_generated_poll)
+				break;
 			pin->work_tx_ts = ts;
 			pin->work_tx_status = CEC_TX_STATUS_OK;
 			wake_up_interruptible(&pin->kthread_waitq);
@@ -217,39 +489,82 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
 		}
 
 		switch (pin->tx_bit % 10) {
-		default:
-			v = pin->tx_msg.msg[pin->tx_bit / 10] &
-				(1 << (7 - (pin->tx_bit % 10)));
+		default: {
+			/*
+			 * In the CEC_ERROR_INJ_TX_ADD_BYTES case we transmit
+			 * extra bytes, so pin->tx_bit / 10 can become >= 16.
+			 * Generate bit values for those extra bytes instead
+			 * of reading them from the transmit buffer.
+			 */
+			unsigned int idx = (pin->tx_bit / 10);
+			u8 val = idx;
+
+			if (idx < pin->tx_msg.len)
+				val = pin->tx_msg.msg[idx];
+			v = val & (1 << (7 - (pin->tx_bit % 10)));
+
 			pin->state = v ? CEC_ST_TX_DATA_BIT_1_LOW :
-				CEC_ST_TX_DATA_BIT_0_LOW;
+					 CEC_ST_TX_DATA_BIT_0_LOW;
 			break;
-		case 8:
-			v = pin->tx_bit / 10 == pin->tx_msg.len - 1;
+		}
+		case EOM_BIT: {
+			unsigned int tot_len = pin->tx_msg.len +
+					       pin->tx_extra_bytes;
+			unsigned int tx_byte_idx = pin->tx_bit / 10;
+
+			v = !pin->tx_post_eom && tx_byte_idx == tot_len - 1;
+			if (tot_len > 1 && tx_byte_idx == tot_len - 2 &&
+			    tx_early_eom(pin)) {
+				/* Error injection: set EOM one byte early */
+				v = true;
+				pin->tx_post_eom = true;
+			} else if (v && tx_no_eom(pin)) {
+				/* Error injection: no EOM */
+				v = false;
+			}
 			pin->state = v ? CEC_ST_TX_DATA_BIT_1_LOW :
-				CEC_ST_TX_DATA_BIT_0_LOW;
+					 CEC_ST_TX_DATA_BIT_0_LOW;
 			break;
-		case 9:
+		}
+		case ACK_BIT:
 			pin->state = CEC_ST_TX_DATA_BIT_1_LOW;
 			break;
 		}
+		if (tx_custom_bit(pin))
+			pin->state = CEC_ST_TX_DATA_BIT_LOW_CUSTOM;
 		cec_pin_low(pin);
 		break;
 
 	case CEC_ST_TX_DATA_BIT_0_LOW:
 	case CEC_ST_TX_DATA_BIT_1_LOW:
 		v = pin->state == CEC_ST_TX_DATA_BIT_1_LOW;
-		pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH :
-			CEC_ST_TX_DATA_BIT_0_HIGH;
-		is_ack_bit = pin->tx_bit % 10 == 9;
-		if (v && (pin->tx_bit < 4 || is_ack_bit))
+		is_ack_bit = pin->tx_bit % 10 == ACK_BIT;
+		if (v && (pin->tx_bit < 4 || is_ack_bit)) {
 			pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE;
+		} else if (!is_ack_bit && tx_short_bit(pin)) {
+			/* Error Injection: send an invalid (too short) bit */
+			pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH_SHORT :
+					 CEC_ST_TX_DATA_BIT_0_HIGH_SHORT;
+		} else if (!is_ack_bit && tx_long_bit(pin)) {
+			/* Error Injection: send an invalid (too long) bit */
+			pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH_LONG :
+					 CEC_ST_TX_DATA_BIT_0_HIGH_LONG;
+		} else {
+			pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH :
+					 CEC_ST_TX_DATA_BIT_0_HIGH;
+		}
+		cec_pin_high(pin);
+		break;
+
+	case CEC_ST_TX_DATA_BIT_LOW_CUSTOM:
+		pin->state = CEC_ST_TX_DATA_BIT_HIGH_CUSTOM;
 		cec_pin_high(pin);
 		break;
 
 	case CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE:
 		/* Read the CEC value at the sample time */
 		v = cec_pin_read(pin);
-		is_ack_bit = pin->tx_bit % 10 == 9;
+		is_ack_bit = pin->tx_bit % 10 == ACK_BIT;
 		/*
 		 * If v == 0 and we're within the first 4 bits
 		 * of the initiator, then someone else started
@@ -258,7 +573,7 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
 		 * transmitter has more leading 0 bits in the
 		 * initiator).
 		 */
-		if (!v && !is_ack_bit) {
+		if (!v && !is_ack_bit && !pin->tx_generated_poll) {
 			pin->tx_msg.len = 0;
 			pin->work_tx_ts = ts;
 			pin->work_tx_status = CEC_TX_STATUS_ARB_LOST;
@@ -267,18 +582,27 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
 			pin->tx_bit = 0;
 			memset(pin->rx_msg.msg, 0, sizeof(pin->rx_msg.msg));
 			pin->rx_msg.msg[0] = pin->tx_msg.msg[0];
-			pin->rx_msg.msg[0] &= ~(1 << (7 - pin->rx_bit));
+			pin->rx_msg.msg[0] &= (0xff << (8 - pin->rx_bit));
 			pin->rx_msg.len = 0;
+			pin->ts = ktime_sub_us(ts, CEC_TIM_DATA_BIT_SAMPLE);
 			pin->state = CEC_ST_RX_DATA_POST_SAMPLE;
 			pin->rx_bit++;
 			break;
 		}
 		pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE;
+		if (!is_ack_bit && tx_short_bit(pin)) {
+			/* Error Injection: send an invalid (too short) bit */
+			pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT;
+		} else if (!is_ack_bit && tx_long_bit(pin)) {
+			/* Error Injection: send an invalid (too long) bit */
+			pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG;
+		}
 		if (!is_ack_bit)
 			break;
 		/* Was the message ACKed? */
 		ack = cec_msg_is_broadcast(&pin->tx_msg) ? v : !v;
-		if (!ack) {
+		if (!ack && !pin->tx_ignore_nack_until_eom &&
+		    pin->tx_bit / 10 < pin->tx_msg.len && !pin->tx_post_eom) {
 			/*
 			 * Note: the CEC spec is ambiguous regarding
 			 * what action to take when a NACK appears
@@ -295,6 +619,15 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
 		}
 		break;
 
+	case CEC_ST_TX_PULSE_LOW_CUSTOM:
+		cec_pin_high(pin);
+		pin->state = CEC_ST_TX_PULSE_HIGH_CUSTOM;
+		break;
+
+	case CEC_ST_TX_PULSE_HIGH_CUSTOM:
+		cec_pin_to_idle(pin);
+		break;
+
 	default:
 		break;
 	}
@@ -322,6 +655,7 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
 	bool ack;
 	bool bcast, for_us;
 	u8 dest;
+	u8 poll;
 
 	switch (pin->state) {
 	/* Receive states */
@@ -331,24 +665,54 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
 			break;
 		pin->state = CEC_ST_RX_START_BIT_HIGH;
 		delta = ktime_us_delta(ts, pin->ts);
-		pin->ts = ts;
 		/* Start bit low is too short, go back to idle */
-		if (delta < CEC_TIM_START_BIT_LOW_MIN -
-			    CEC_TIM_IDLE_SAMPLE) {
+		if (delta < CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_IDLE_SAMPLE) {
+			if (!pin->rx_start_bit_low_too_short_cnt++) {
+				pin->rx_start_bit_low_too_short_ts = pin->ts;
+				pin->rx_start_bit_low_too_short_delta = delta;
+			}
 			cec_pin_to_idle(pin);
+			break;
+		}
+		if (rx_arb_lost(pin, &poll)) {
+			cec_msg_init(&pin->tx_msg, poll >> 4, poll & 0xf);
+			pin->tx_generated_poll = true;
+			pin->tx_extra_bytes = 0;
+			pin->state = CEC_ST_TX_START_BIT_HIGH;
+			pin->ts = ts;
 		}
 		break;
 
 	case CEC_ST_RX_START_BIT_HIGH:
 		v = cec_pin_read(pin);
 		delta = ktime_us_delta(ts, pin->ts);
-		if (v && delta > CEC_TIM_START_BIT_TOTAL_MAX -
-				 CEC_TIM_START_BIT_LOW_MIN) {
+		/*
+		 * Unfortunately the spec does not specify when to give up
+		 * and go to idle. We just pick TOTAL_LONG.
+		 */
+		if (v && delta > CEC_TIM_START_BIT_TOTAL_LONG) {
+			pin->rx_start_bit_too_long_cnt++;
 			cec_pin_to_idle(pin);
 			break;
 		}
 		if (v)
 			break;
+		/* Start bit is too short, go back to idle */
+		if (delta < CEC_TIM_START_BIT_TOTAL_MIN - CEC_TIM_IDLE_SAMPLE) {
+			if (!pin->rx_start_bit_too_short_cnt++) {
+				pin->rx_start_bit_too_short_ts = pin->ts;
+				pin->rx_start_bit_too_short_delta = delta;
+			}
+			cec_pin_to_idle(pin);
+			break;
+		}
+		if (rx_low_drive(pin)) {
+			/* Error injection: go to low drive */
+			cec_pin_low(pin);
+			pin->state = CEC_ST_RX_LOW_DRIVE;
+			pin->rx_low_drive_cnt++;
+			break;
+		}
 		pin->state = CEC_ST_RX_DATA_SAMPLE;
 		pin->ts = ts;
 		pin->rx_eom = false;
@@ -363,36 +727,55 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
 				pin->rx_msg.msg[pin->rx_bit / 10] |=
 					v << (7 - (pin->rx_bit % 10));
 			break;
-		case 8:
+		case EOM_BIT:
 			pin->rx_eom = v;
 			pin->rx_msg.len = pin->rx_bit / 10 + 1;
 			break;
-		case 9:
+		case ACK_BIT:
 			break;
 		}
 		pin->rx_bit++;
 		break;
 
 	case CEC_ST_RX_DATA_POST_SAMPLE:
-		pin->state = CEC_ST_RX_DATA_HIGH;
+		pin->state = CEC_ST_RX_DATA_WAIT_FOR_LOW;
 		break;
 
-	case CEC_ST_RX_DATA_HIGH:
+	case CEC_ST_RX_DATA_WAIT_FOR_LOW:
 		v = cec_pin_read(pin);
 		delta = ktime_us_delta(ts, pin->ts);
-		if (v && delta > CEC_TIM_DATA_BIT_TOTAL_MAX) {
+		/*
+		 * Unfortunately the spec does not specify when to give up
+		 * and go to idle. We just pick TOTAL_LONG.
+		 */
+		if (v && delta > CEC_TIM_DATA_BIT_TOTAL_LONG) {
+			pin->rx_data_bit_too_long_cnt++;
 			cec_pin_to_idle(pin);
 			break;
 		}
 		if (v)
 			break;
+
+		if (rx_low_drive(pin)) {
+			/* Error injection: go to low drive */
+			cec_pin_low(pin);
+			pin->state = CEC_ST_RX_LOW_DRIVE;
+			pin->rx_low_drive_cnt++;
+			break;
+		}
+
 		/*
 		 * Go to low drive state when the total bit time is
 		 * too short.
 		 */
 		if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN) {
+			if (!pin->rx_data_bit_too_short_cnt++) {
+				pin->rx_data_bit_too_short_ts = pin->ts;
+				pin->rx_data_bit_too_short_delta = delta;
+			}
 			cec_pin_low(pin);
-			pin->state = CEC_ST_LOW_DRIVE;
+			pin->state = CEC_ST_RX_LOW_DRIVE;
+			pin->rx_low_drive_cnt++;
 			break;
 		}
 		pin->ts = ts;
@@ -408,6 +791,11 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
 		/* ACK bit value */
 		ack = bcast ? 1 : !for_us;
 
+		if (for_us && rx_nack(pin)) {
+			/* Error injection: toggle the ACK bit */
+			ack = !ack;
+		}
+
 		if (ack) {
 			/* No need to write to the bus, just wait */
 			pin->state = CEC_ST_RX_ACK_HIGH_POST;
@@ -434,7 +822,7 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
 			break;
 		}
 		pin->rx_bit++;
-		pin->state = CEC_ST_RX_DATA_HIGH;
+		pin->state = CEC_ST_RX_DATA_WAIT_FOR_LOW;
 		break;
 
 	case CEC_ST_RX_ACK_FINISH:
@@ -456,6 +844,7 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
 	struct cec_adapter *adap = pin->adap;
 	ktime_t ts;
 	s32 delta;
+	u32 usecs;
 
 	ts = ktime_get();
 	if (ktime_to_ns(pin->timer_ts)) {
@@ -503,13 +892,27 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
 	/* Transmit states */
 	case CEC_ST_TX_WAIT_FOR_HIGH:
 	case CEC_ST_TX_START_BIT_LOW:
-	case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE:
-	case CEC_ST_TX_DATA_BIT_0_HIGH:
-	case CEC_ST_TX_DATA_BIT_1_HIGH:
 	case CEC_ST_TX_START_BIT_HIGH:
+	case CEC_ST_TX_START_BIT_HIGH_SHORT:
+	case CEC_ST_TX_START_BIT_HIGH_LONG:
+	case CEC_ST_TX_START_BIT_LOW_CUSTOM:
+	case CEC_ST_TX_START_BIT_HIGH_CUSTOM:
 	case CEC_ST_TX_DATA_BIT_0_LOW:
+	case CEC_ST_TX_DATA_BIT_0_HIGH:
+	case CEC_ST_TX_DATA_BIT_0_HIGH_SHORT:
+	case CEC_ST_TX_DATA_BIT_0_HIGH_LONG:
 	case CEC_ST_TX_DATA_BIT_1_LOW:
+	case CEC_ST_TX_DATA_BIT_1_HIGH:
+	case CEC_ST_TX_DATA_BIT_1_HIGH_SHORT:
+	case CEC_ST_TX_DATA_BIT_1_HIGH_LONG:
 	case CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE:
+	case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE:
+	case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT:
+	case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG:
+	case CEC_ST_TX_DATA_BIT_LOW_CUSTOM:
+	case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM:
+	case CEC_ST_TX_PULSE_LOW_CUSTOM:
+	case CEC_ST_TX_PULSE_HIGH_CUSTOM:
 		cec_pin_tx_states(pin, ts);
 		break;
 
@@ -518,7 +921,7 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
 	case CEC_ST_RX_START_BIT_HIGH:
 	case CEC_ST_RX_DATA_SAMPLE:
 	case CEC_ST_RX_DATA_POST_SAMPLE:
-	case CEC_ST_RX_DATA_HIGH:
+	case CEC_ST_RX_DATA_WAIT_FOR_LOW:
 	case CEC_ST_RX_ACK_LOW:
 	case CEC_ST_RX_ACK_LOW_POST:
 	case CEC_ST_RX_ACK_HIGH_POST:
@@ -545,7 +948,10 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
 			if (delta / CEC_TIM_DATA_BIT_TOTAL >
 			    pin->tx_signal_free_time) {
 				pin->tx_nacked = false;
-				pin->state = CEC_ST_TX_START_BIT_LOW;
+				if (tx_custom_start(pin))
+					pin->state = CEC_ST_TX_START_BIT_LOW_CUSTOM;
+				else
+					pin->state = CEC_ST_TX_START_BIT_LOW;
 				/* Generate start bit */
 				cec_pin_low(pin);
 				break;
@@ -555,6 +961,13 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
 				pin->state = CEC_ST_TX_WAIT;
 			break;
 		}
+		if (pin->tx_custom_pulse && pin->state == CEC_ST_IDLE) {
+			pin->tx_custom_pulse = false;
+			/* Generate custom pulse */
+			cec_pin_low(pin);
+			pin->state = CEC_ST_TX_PULSE_LOW_CUSTOM;
+			break;
+		}
 		if (pin->state != CEC_ST_IDLE || pin->ops->enable_irq == NULL ||
 		    pin->enable_irq_failed || adap->is_configuring ||
 		    adap->is_configured || adap->monitor_all_cnt)
@@ -565,21 +978,40 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
 		wake_up_interruptible(&pin->kthread_waitq);
 		return HRTIMER_NORESTART;
 
-	case CEC_ST_LOW_DRIVE:
+	case CEC_ST_TX_LOW_DRIVE:
+	case CEC_ST_RX_LOW_DRIVE:
+		cec_pin_high(pin);
 		cec_pin_to_idle(pin);
 		break;
 
 	default:
 		break;
 	}
-	if (!adap->monitor_pin_cnt || states[pin->state].usecs <= 150) {
+
+	switch (pin->state) {
+	case CEC_ST_TX_START_BIT_LOW_CUSTOM:
+	case CEC_ST_TX_DATA_BIT_LOW_CUSTOM:
+	case CEC_ST_TX_PULSE_LOW_CUSTOM:
+		usecs = pin->tx_custom_low_usecs;
+		break;
+	case CEC_ST_TX_START_BIT_HIGH_CUSTOM:
+	case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM:
+	case CEC_ST_TX_PULSE_HIGH_CUSTOM:
+		usecs = pin->tx_custom_high_usecs;
+		break;
+	default:
+		usecs = states[pin->state].usecs;
+		break;
+	}
+
+	if (!adap->monitor_pin_cnt || usecs <= 150) {
 		pin->wait_usecs = 0;
-		pin->timer_ts = ktime_add_us(ts, states[pin->state].usecs);
+		pin->timer_ts = ktime_add_us(ts, usecs);
 		hrtimer_forward_now(timer,
-				ns_to_ktime(states[pin->state].usecs * 1000));
+				ns_to_ktime(usecs * 1000));
 		return HRTIMER_RESTART;
 	}
-	pin->wait_usecs = states[pin->state].usecs - 100;
+	pin->wait_usecs = usecs - 100;
 	pin->timer_ts = ktime_add_us(ts, 100);
 	hrtimer_forward_now(timer, ns_to_ktime(100000));
 	return HRTIMER_RESTART;
@@ -596,12 +1028,25 @@ static int cec_pin_thread_func(void *_adap)
 			pin->work_rx_msg.len ||
 			pin->work_tx_status ||
 			atomic_read(&pin->work_irq_change) ||
-			atomic_read(&pin->work_pin_events));
+			atomic_read(&pin->work_pin_num_events));
 
 		if (pin->work_rx_msg.len) {
-			cec_received_msg_ts(adap, &pin->work_rx_msg,
+			struct cec_msg *msg = &pin->work_rx_msg;
+
+			if (msg->len > 1 && msg->len < CEC_MAX_MSG_SIZE &&
+			    rx_add_byte(pin)) {
+				/* Error injection: add byte to the message */
+				msg->msg[msg->len++] = 0x55;
+			}
+			if (msg->len > 2 && rx_remove_byte(pin)) {
+				/* Error injection: remove byte from message */
+				msg->len--;
+			}
+			if (msg->len > CEC_MAX_MSG_SIZE)
+				msg->len = CEC_MAX_MSG_SIZE;
+			cec_received_msg_ts(adap, msg,
 				ns_to_ktime(pin->work_rx_msg.rx_ts));
-			pin->work_rx_msg.len = 0;
+			msg->len = 0;
 		}
 		if (pin->work_tx_status) {
 			unsigned int tx_status = pin->work_tx_status;
@@ -611,14 +1056,16 @@ static int cec_pin_thread_func(void *_adap)
 						     pin->work_tx_ts);
 		}
 
-		while (atomic_read(&pin->work_pin_events)) {
+		while (atomic_read(&pin->work_pin_num_events)) {
 			unsigned int idx = pin->work_pin_events_rd;
+			u8 v = pin->work_pin_events[idx];
 
 			cec_queue_pin_cec_event(adap,
-						pin->work_pin_is_high[idx],
+						v & CEC_PIN_EVENT_FL_IS_HIGH,
+						v & CEC_PIN_EVENT_FL_DROPPED,
 						pin->work_pin_ts[idx]);
 			pin->work_pin_events_rd = (idx + 1) % CEC_NUM_PIN_EVENTS;
-			atomic_dec(&pin->work_pin_events);
+			atomic_dec(&pin->work_pin_num_events);
 		}
 
 		switch (atomic_xchg(&pin->work_irq_change,
@@ -654,8 +1101,9 @@ static int cec_pin_adap_enable(struct cec_adapter *adap, bool enable)
 
 	pin->enabled = enable;
 	if (enable) {
-		atomic_set(&pin->work_pin_events, 0);
+		atomic_set(&pin->work_pin_num_events, 0);
 		pin->work_pin_events_rd = pin->work_pin_events_wr = 0;
+		pin->work_pin_events_dropped = false;
 		cec_pin_read(pin);
 		cec_pin_to_idle(pin);
 		pin->tx_msg.len = 0;
@@ -692,23 +1140,37 @@ static int cec_pin_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
 	return 0;
 }
 
+void cec_pin_start_timer(struct cec_pin *pin)
+{
+	if (pin->state != CEC_ST_RX_IRQ)
+		return;
+
+	atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED);
+	pin->ops->disable_irq(pin->adap);
+	cec_pin_high(pin);
+	cec_pin_to_idle(pin);
+	hrtimer_start(&pin->timer, ns_to_ktime(0), HRTIMER_MODE_REL);
+}
+
 static int cec_pin_adap_transmit(struct cec_adapter *adap, u8 attempts,
 				      u32 signal_free_time, struct cec_msg *msg)
 {
 	struct cec_pin *pin = adap->pin;
 
 	pin->tx_signal_free_time = signal_free_time;
+	pin->tx_extra_bytes = 0;
 	pin->tx_msg = *msg;
+	if (msg->len > 1) {
+		/* Error injection: add byte to the message */
+		pin->tx_extra_bytes = tx_add_bytes(pin);
+	}
+	if (msg->len > 2 && tx_remove_byte(pin)) {
+		/* Error injection: remove byte from the message */
+		pin->tx_msg.len--;
+	}
 	pin->work_tx_status = 0;
 	pin->tx_bit = 0;
-	if (pin->state == CEC_ST_RX_IRQ) {
-		atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED);
-		pin->ops->disable_irq(adap);
-		cec_pin_high(pin);
-		cec_pin_to_idle(pin);
-		hrtimer_start(&pin->timer, ns_to_ktime(0),
-			      HRTIMER_MODE_REL);
-	}
+	cec_pin_start_timer(pin);
 	return 0;
 }
 
@@ -717,10 +1179,12 @@ static void cec_pin_adap_status(struct cec_adapter *adap,
 {
 	struct cec_pin *pin = adap->pin;
 
-	seq_printf(file, "state:   %s\n", states[pin->state].name);
-	seq_printf(file, "tx_bit:  %d\n", pin->tx_bit);
-	seq_printf(file, "rx_bit:  %d\n", pin->rx_bit);
+	seq_printf(file, "state: %s\n", states[pin->state].name);
+	seq_printf(file, "tx_bit: %d\n", pin->tx_bit);
+	seq_printf(file, "rx_bit: %d\n", pin->rx_bit);
 	seq_printf(file, "cec pin: %d\n", pin->ops->read(adap));
+	seq_printf(file, "cec pin events dropped: %u\n",
+		   pin->work_pin_events_dropped_cnt);
 	seq_printf(file, "irq failed: %d\n", pin->enable_irq_failed);
 	if (pin->timer_100ms_overruns) {
 		seq_printf(file, "timer overruns > 100ms: %u of %u\n",
@@ -732,11 +1196,45 @@ static void cec_pin_adap_status(struct cec_adapter *adap,
 		seq_printf(file, "avg timer overrun: %u usecs\n",
 			   pin->timer_sum_overrun / pin->timer_100ms_overruns);
 	}
+	if (pin->rx_start_bit_low_too_short_cnt)
+		seq_printf(file,
+			   "rx start bit low too short: %u (delta %u, ts %llu)\n",
+			   pin->rx_start_bit_low_too_short_cnt,
+			   pin->rx_start_bit_low_too_short_delta,
+			   pin->rx_start_bit_low_too_short_ts);
+	if (pin->rx_start_bit_too_short_cnt)
+		seq_printf(file,
+			   "rx start bit too short: %u (delta %u, ts %llu)\n",
+			   pin->rx_start_bit_too_short_cnt,
+			   pin->rx_start_bit_too_short_delta,
+			   pin->rx_start_bit_too_short_ts);
+	if (pin->rx_start_bit_too_long_cnt)
+		seq_printf(file, "rx start bit too long: %u\n",
+			   pin->rx_start_bit_too_long_cnt);
+	if (pin->rx_data_bit_too_short_cnt)
+		seq_printf(file,
+			   "rx data bit too short: %u (delta %u, ts %llu)\n",
+			   pin->rx_data_bit_too_short_cnt,
+			   pin->rx_data_bit_too_short_delta,
+			   pin->rx_data_bit_too_short_ts);
+	if (pin->rx_data_bit_too_long_cnt)
+		seq_printf(file, "rx data bit too long: %u\n",
+			   pin->rx_data_bit_too_long_cnt);
+	seq_printf(file, "rx initiated low drive: %u\n", pin->rx_low_drive_cnt);
+	seq_printf(file, "tx detected low drive: %u\n", pin->tx_low_drive_cnt);
+	pin->work_pin_events_dropped_cnt = 0;
 	pin->timer_cnt = 0;
 	pin->timer_100ms_overruns = 0;
 	pin->timer_300ms_overruns = 0;
 	pin->timer_max_overrun = 0;
 	pin->timer_sum_overrun = 0;
+	pin->rx_start_bit_low_too_short_cnt = 0;
+	pin->rx_start_bit_too_short_cnt = 0;
+	pin->rx_start_bit_too_long_cnt = 0;
+	pin->rx_data_bit_too_short_cnt = 0;
+	pin->rx_data_bit_too_long_cnt = 0;
+	pin->rx_low_drive_cnt = 0;
+	pin->tx_low_drive_cnt = 0;
 	if (pin->ops->status)
 		pin->ops->status(adap, file);
 }
@@ -778,6 +1276,10 @@ static const struct cec_adap_ops cec_pin_adap_ops = {
 	.adap_transmit = cec_pin_adap_transmit,
 	.adap_status = cec_pin_adap_status,
 	.adap_free = cec_pin_adap_free,
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+	.error_inj_parse_line = cec_pin_error_inj_parse_line,
+	.error_inj_show = cec_pin_error_inj_show,
+#endif
 };
 
 struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops,
@@ -792,6 +1294,8 @@ struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops,
 	hrtimer_init(&pin->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 	pin->timer.function = cec_pin_timer;
 	init_waitqueue_head(&pin->kthread_waitq);
+	pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
+	pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
 
 	adap = cec_allocate_adapter(&cec_pin_adap_ops, priv, name,
 			    caps | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN,

+ 1 - 13
drivers/media/cec/cec-priv.h

@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * cec-priv.h - HDMI Consumer Electronics Control internal header
  *
  * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #ifndef _CEC_PRIV_H

+ 22 - 11
drivers/media/common/siano/smscoreapi.c

@@ -631,7 +631,8 @@ smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer,
 
 	cb->p = buffer;
 	cb->offset_in_common = buffer - (u8 *) common_buffer;
-	cb->phys = common_buffer_phys + cb->offset_in_common;
+	if (common_buffer_phys)
+		cb->phys = common_buffer_phys + cb->offset_in_common;
 
 	return cb;
 }
@@ -690,17 +691,21 @@ int smscore_register_device(struct smsdevice_params_t *params,
 
 	/* alloc common buffer */
 	dev->common_buffer_size = params->buffer_size * params->num_buffers;
-	dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size,
-						&dev->common_buffer_phys,
-						GFP_KERNEL | GFP_DMA);
-	if (!dev->common_buffer) {
+	if (params->usb_device)
+		buffer = kzalloc(dev->common_buffer_size, GFP_KERNEL);
+	else
+		buffer = dma_alloc_coherent(params->device,
+					    dev->common_buffer_size,
+					    &dev->common_buffer_phys,
+					    GFP_KERNEL | GFP_DMA);
+	if (!buffer) {
 		smscore_unregister_device(dev);
 		return -ENOMEM;
 	}
+	dev->common_buffer = buffer;
 
 	/* prepare dma buffers */
-	for (buffer = dev->common_buffer;
-	     dev->num_buffers < params->num_buffers;
+	for (; dev->num_buffers < params->num_buffers;
 	     dev->num_buffers++, buffer += params->buffer_size) {
 		struct smscore_buffer_t *cb;
 
@@ -720,6 +725,7 @@ int smscore_register_device(struct smsdevice_params_t *params,
 	dev->board_id = SMS_BOARD_UNKNOWN;
 	dev->context = params->context;
 	dev->device = params->device;
+	dev->usb_device = params->usb_device;
 	dev->setmode_handler = params->setmode_handler;
 	dev->detectmode_handler = params->detectmode_handler;
 	dev->sendrequest_handler = params->sendrequest_handler;
@@ -1231,10 +1237,15 @@ void smscore_unregister_device(struct smscore_device_t *coredev)
 
 	pr_debug("freed %d buffers\n", num_buffers);
 
-	if (coredev->common_buffer)
-		dma_free_coherent(NULL, coredev->common_buffer_size,
-			coredev->common_buffer, coredev->common_buffer_phys);
-
+	if (coredev->common_buffer) {
+		if (coredev->usb_device)
+			kfree(coredev->common_buffer);
+		else
+			dma_free_coherent(coredev->device,
+					  coredev->common_buffer_size,
+					  coredev->common_buffer,
+					  coredev->common_buffer_phys);
+	}
 	kfree(coredev->fw_buf);
 
 	list_del(&coredev->entry);

+ 2 - 0
drivers/media/common/siano/smscoreapi.h

@@ -134,6 +134,7 @@ struct smscore_buffer_t {
 
 struct smsdevice_params_t {
 	struct device	*device;
+	struct usb_device	*usb_device;
 
 	int				buffer_size;
 	int				num_buffers;
@@ -176,6 +177,7 @@ struct smscore_device_t {
 
 	void *context;
 	struct device *device;
+	struct usb_device *usb_device;
 
 	char devpath[32];
 	unsigned long device_flags;

+ 1 - 13
drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c

@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * v4l2-tpg-colors.c - A table that converts colors to various colorspaces
  *
@@ -20,19 +21,6 @@
  * in order to preserve precision.
  *
  * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #include <linux/videodev2.h>

+ 3 - 15
drivers/media/common/v4l2-tpg/v4l2-tpg-core.c

@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * v4l2-tpg-core.c - Test Pattern Generator
  *
@@ -5,19 +6,6 @@
  * vivi.c source for the copyright information of those functions.
  *
  * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
  */
 
 #include <linux/module.h>
@@ -1155,13 +1143,13 @@ static void gen_twopix(struct tpg_data *tpg,
 	case V4L2_PIX_FMT_NV24:
 		buf[0][offset] = r_y_h;
 		buf[1][2 * offset] = g_u_s;
-		buf[1][2 * offset + 1] = b_v;
+		buf[1][(2 * offset + 1) % 8] = b_v;
 		break;
 
 	case V4L2_PIX_FMT_NV42:
 		buf[0][offset] = r_y_h;
 		buf[1][2 * offset] = b_v;
-		buf[1][2 * offset + 1] = g_u_s;
+		buf[1][(2 * offset + 1) %8] = g_u_s;
 		break;
 
 	case V4L2_PIX_FMT_YUYV:

+ 9 - 0
drivers/media/common/videobuf2/videobuf2-core.c

@@ -1696,6 +1696,15 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
 	for (i = 0; i < q->num_buffers; ++i) {
 		struct vb2_buffer *vb = q->bufs[i];
 
+		if (vb->state == VB2_BUF_STATE_PREPARED ||
+		    vb->state == VB2_BUF_STATE_QUEUED) {
+			unsigned int plane;
+
+			for (plane = 0; plane < vb->num_planes; ++plane)
+				call_void_memop(vb, finish,
+						vb->planes[plane].mem_priv);
+		}
+
 		if (vb->state != VB2_BUF_STATE_DEQUEUED) {
 			vb->state = VB2_BUF_STATE_PREPARED;
 			call_void_vb_qop(vb, buf_finish, vb);

+ 1 - 1
drivers/media/common/videobuf2/videobuf2-vmalloc.c

@@ -106,7 +106,7 @@ static void *vb2_vmalloc_get_userptr(struct device *dev, unsigned long vaddr,
 			if (nums[i-1] + 1 != nums[i])
 				goto fail_map;
 		buf->vaddr = (__force void *)
-				ioremap_nocache(nums[0] << PAGE_SHIFT, size);
+			ioremap_nocache(__pfn_to_phys(nums[0]), size + offset);
 	} else {
 		buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1,
 					PAGE_KERNEL);

+ 2 - 2
drivers/media/dvb-core/dvb_ca_en50221.c

@@ -1254,8 +1254,8 @@ static void dvb_ca_en50221_thread_state_machine(struct dvb_ca_private *ca,
 		ca->pub->slot_ts_enable(ca->pub, slot);
 		sl->slot_state = DVB_CA_SLOTSTATE_RUNNING;
 		dvb_ca_en50221_thread_update_delay(ca);
-		pr_err("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n",
-		       ca->dvbdev->adapter->num);
+		pr_info("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n",
+			ca->dvbdev->adapter->num);
 		break;
 
 	case DVB_CA_SLOTSTATE_RUNNING:

+ 2 - 2
drivers/media/dvb-core/dvb_frontend.c

@@ -2294,7 +2294,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
 		if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
 			return -EINVAL;
 
-		tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp));
+		tvp = memdup_user((void __user *)tvps->props, tvps->num * sizeof(*tvp));
 		if (IS_ERR(tvp))
 			return PTR_ERR(tvp);
 
@@ -2328,7 +2328,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
 		if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
 			return -EINVAL;
 
-		tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp));
+		tvp = memdup_user((void __user *)tvps->props, tvps->num * sizeof(*tvp));
 		if (IS_ERR(tvp))
 			return PTR_ERR(tvp);
 

+ 50 - 0
drivers/media/dvb-core/dvbdev.c

@@ -24,6 +24,7 @@
 #include <linux/string.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/device.h>
@@ -941,6 +942,55 @@ out:
 	return err;
 }
 
+#if IS_ENABLED(CONFIG_I2C)
+struct i2c_client *dvb_module_probe(const char *module_name,
+				    const char *name,
+				    struct i2c_adapter *adap,
+				    unsigned char addr,
+				    void *platform_data)
+{
+	struct i2c_client *client;
+	struct i2c_board_info *board_info;
+
+	board_info = kzalloc(sizeof(*board_info), GFP_KERNEL);
+	if (!board_info)
+		return NULL;
+
+	if (name)
+		strlcpy(board_info->type, name, I2C_NAME_SIZE);
+	else
+		strlcpy(board_info->type, module_name, I2C_NAME_SIZE);
+
+	board_info->addr = addr;
+	board_info->platform_data = platform_data;
+	request_module(module_name);
+	client = i2c_new_device(adap, board_info);
+	if (client == NULL || client->dev.driver == NULL) {
+		kfree(board_info);
+		return NULL;
+	}
+
+	if (!try_module_get(client->dev.driver->owner)) {
+		i2c_unregister_device(client);
+		client = NULL;
+	}
+
+	kfree(board_info);
+	return client;
+}
+EXPORT_SYMBOL_GPL(dvb_module_probe);
+
+void dvb_module_release(struct i2c_client *client)
+{
+	if (!client)
+		return;
+
+	module_put(client->dev.driver->owner);
+	i2c_unregister_device(client);
+}
+EXPORT_SYMBOL_GPL(dvb_module_release);
+#endif
+
 static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct dvb_device *dvbdev = dev_get_drvdata(dev);

+ 24 - 8
drivers/media/dvb-frontends/Kconfig

@@ -462,7 +462,7 @@ config DVB_TDA10048
 
 config DVB_AF9013
 	tristate "Afatech AF9013 demodulator"
-	depends on DVB_CORE && I2C
+	depends on DVB_CORE && I2C && I2C_MUX
 	select REGMAP
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 	help
@@ -546,6 +546,8 @@ config DVB_GP8PSK_FE
 	depends on DVB_CORE
 	default DVB_USB_GP8PSK
 
+source "drivers/media/dvb-frontends/cxd2880/Kconfig"
+
 comment "DVB-C (cable) frontends"
 	depends on DVB_CORE
 
@@ -822,13 +824,6 @@ config DVB_A8293
 	depends on DVB_CORE && I2C
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 
-config DVB_SP2
-	tristate "CIMaX SP2"
-	depends on DVB_CORE && I2C
-	default m if !MEDIA_SUBDRV_AUTOSELECT
-	help
-	  CIMaX SP2/SP2HF Common Interface module.
-
 config DVB_LGS8GL5
 	tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
 	depends on DVB_CORE && I2C
@@ -904,6 +899,27 @@ config DVB_HELENE
 	help
 	  Say Y when you want to support this frontend.
 
+comment "Common Interface (EN50221) controller drivers"
+	depends on DVB_CORE
+
+config DVB_CXD2099
+	tristate "CXD2099AR Common Interface driver"
+	depends on DVB_CORE && I2C
+	select REGMAP_I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  A driver for the CI controller currently found mostly on
+	  Digital Devices DuoFlex CI (single) addon modules.
+
+	  Say Y when you want to support these devices.
+
+config DVB_SP2
+	tristate "CIMaX SP2"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  CIMaX SP2/SP2HF Common Interface module.
+
 comment "Tools to develop new frontends"
 
 config DVB_DUMMY_FE

+ 2 - 0
drivers/media/dvb-frontends/Makefile

@@ -129,3 +129,5 @@ obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
 obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
 obj-$(CONFIG_DVB_HELENE) += helene.o
 obj-$(CONFIG_DVB_ZD1301_DEMOD) += zd1301_demod.o
+obj-$(CONFIG_DVB_CXD2099) += cxd2099.o
+obj-$(CONFIG_DVB_CXD2880) += cxd2880/

+ 465 - 444
drivers/media/dvb-frontends/af9013.c

@@ -23,6 +23,7 @@
 struct af9013_state {
 	struct i2c_client *client;
 	struct regmap *regmap;
+	struct i2c_mux_core *muxc;
 	struct dvb_frontend fe;
 	u32 clk;
 	u8 tuner;
@@ -33,20 +34,20 @@ struct af9013_state {
 	u8 api_version[4];
 	u8 gpio[4];
 
-	/* tuner/demod RF and IF AGC limits used for signal strength calc */
-	u8 signal_strength_en, rf_50, rf_80, if_50, if_80;
-	u16 signal_strength;
-	u32 ber;
-	u32 ucblocks;
-	u16 snr;
 	u32 bandwidth_hz;
 	enum fe_status fe_status;
+	/* RF and IF AGC limits used for signal strength calc */
+	u8 strength_en, rf_agc_50, rf_agc_80, if_agc_50, if_agc_80;
 	unsigned long set_frontend_jiffies;
 	unsigned long read_status_jiffies;
+	unsigned long strength_jiffies;
+	unsigned long cnr_jiffies;
+	unsigned long ber_ucb_jiffies;
+	u16 dvbv3_snr;
+	u16 dvbv3_strength;
+	u32 dvbv3_ber;
+	u32 dvbv3_ucblocks;
 	bool first_tune;
-	bool i2c_gate_state;
-	unsigned int statistics_step:3;
-	struct delayed_work statistics_work;
 };
 
 static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
@@ -101,232 +102,6 @@ err:
 	return ret;
 }
 
-static int af9013_statistics_ber_unc_start(struct dvb_frontend *fe)
-{
-	struct af9013_state *state = fe->demodulator_priv;
-	struct i2c_client *client = state->client;
-	int ret;
-
-	dev_dbg(&client->dev, "\n");
-
-	/* reset and start BER counter */
-	ret = regmap_update_bits(state->regmap, 0xd391, 0x10, 0x10);
-	if (ret)
-		goto err;
-
-	return 0;
-err:
-	dev_dbg(&client->dev, "failed %d\n", ret);
-	return ret;
-}
-
-static int af9013_statistics_ber_unc_result(struct dvb_frontend *fe)
-{
-	struct af9013_state *state = fe->demodulator_priv;
-	struct i2c_client *client = state->client;
-	int ret;
-	unsigned int utmp;
-	u8 buf[5];
-
-	dev_dbg(&client->dev, "\n");
-
-	/* check if error bit count is ready */
-	ret = regmap_read(state->regmap, 0xd391, &utmp);
-	if (ret)
-		goto err;
-
-	if (!((utmp >> 4) & 0x01)) {
-		dev_dbg(&client->dev, "not ready\n");
-		return 0;
-	}
-
-	ret = regmap_bulk_read(state->regmap, 0xd387, buf, 5);
-	if (ret)
-		goto err;
-
-	state->ber = (buf[2] << 16) | (buf[1] << 8) | buf[0];
-	state->ucblocks += (buf[4] << 8) | buf[3];
-
-	return 0;
-err:
-	dev_dbg(&client->dev, "failed %d\n", ret);
-	return ret;
-}
-
-static int af9013_statistics_snr_start(struct dvb_frontend *fe)
-{
-	struct af9013_state *state = fe->demodulator_priv;
-	struct i2c_client *client = state->client;
-	int ret;
-
-	dev_dbg(&client->dev, "\n");
-
-	/* start SNR meas */
-	ret = regmap_update_bits(state->regmap, 0xd2e1, 0x08, 0x08);
-	if (ret)
-		goto err;
-
-	return 0;
-err:
-	dev_dbg(&client->dev, "failed %d\n", ret);
-	return ret;
-}
-
-static int af9013_statistics_snr_result(struct dvb_frontend *fe)
-{
-	struct af9013_state *state = fe->demodulator_priv;
-	struct i2c_client *client = state->client;
-	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-	int ret, i, len;
-	unsigned int utmp;
-	u8 buf[3];
-	u32 snr_val;
-	const struct af9013_snr *uninitialized_var(snr_lut);
-
-	dev_dbg(&client->dev, "\n");
-
-	/* check if SNR ready */
-	ret = regmap_read(state->regmap, 0xd2e1, &utmp);
-	if (ret)
-		goto err;
-
-	if (!((utmp >> 3) & 0x01)) {
-		dev_dbg(&client->dev, "not ready\n");
-		return 0;
-	}
-
-	/* read value */
-	ret = regmap_bulk_read(state->regmap, 0xd2e3, buf, 3);
-	if (ret)
-		goto err;
-
-	snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0];
-
-	/* read current modulation */
-	ret = regmap_read(state->regmap, 0xd3c1, &utmp);
-	if (ret)
-		goto err;
-
-	switch ((utmp >> 6) & 3) {
-	case 0:
-		len = ARRAY_SIZE(qpsk_snr_lut);
-		snr_lut = qpsk_snr_lut;
-		break;
-	case 1:
-		len = ARRAY_SIZE(qam16_snr_lut);
-		snr_lut = qam16_snr_lut;
-		break;
-	case 2:
-		len = ARRAY_SIZE(qam64_snr_lut);
-		snr_lut = qam64_snr_lut;
-		break;
-	default:
-		goto err;
-	}
-
-	for (i = 0; i < len; i++) {
-		utmp = snr_lut[i].snr;
-
-		if (snr_val < snr_lut[i].val)
-			break;
-	}
-	state->snr = utmp * 10; /* dB/10 */
-
-	c->cnr.stat[0].svalue = 1000 * utmp;
-	c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
-
-	return 0;
-err:
-	dev_dbg(&client->dev, "failed %d\n", ret);
-	return ret;
-}
-
-static int af9013_statistics_signal_strength(struct dvb_frontend *fe)
-{
-	struct af9013_state *state = fe->demodulator_priv;
-	struct i2c_client *client = state->client;
-	int ret = 0;
-	u8 buf[2], rf_gain, if_gain;
-	int signal_strength;
-
-	dev_dbg(&client->dev, "\n");
-
-	if (!state->signal_strength_en)
-		return 0;
-
-	ret = regmap_bulk_read(state->regmap, 0xd07c, buf, 2);
-	if (ret)
-		goto err;
-
-	rf_gain = buf[0];
-	if_gain = buf[1];
-
-	signal_strength = (0xffff / \
-		(9 * (state->rf_50 + state->if_50) - \
-		11 * (state->rf_80 + state->if_80))) * \
-		(10 * (rf_gain + if_gain) - \
-		11 * (state->rf_80 + state->if_80));
-	if (signal_strength < 0)
-		signal_strength = 0;
-	else if (signal_strength > 0xffff)
-		signal_strength = 0xffff;
-
-	state->signal_strength = signal_strength;
-
-	return 0;
-err:
-	dev_dbg(&client->dev, "failed %d\n", ret);
-	return ret;
-}
-
-static void af9013_statistics_work(struct work_struct *work)
-{
-	struct af9013_state *state = container_of(work,
-		struct af9013_state, statistics_work.work);
-	unsigned int next_msec;
-
-	/* update only signal strength when demod is not locked */
-	if (!(state->fe_status & FE_HAS_LOCK)) {
-		state->statistics_step = 0;
-		state->ber = 0;
-		state->snr = 0;
-	}
-
-	switch (state->statistics_step) {
-	default:
-		state->statistics_step = 0;
-		/* fall-through */
-	case 0:
-		af9013_statistics_signal_strength(&state->fe);
-		state->statistics_step++;
-		next_msec = 300;
-		break;
-	case 1:
-		af9013_statistics_snr_start(&state->fe);
-		state->statistics_step++;
-		next_msec = 200;
-		break;
-	case 2:
-		af9013_statistics_ber_unc_start(&state->fe);
-		state->statistics_step++;
-		next_msec = 1000;
-		break;
-	case 3:
-		af9013_statistics_snr_result(&state->fe);
-		state->statistics_step++;
-		next_msec = 400;
-		break;
-	case 4:
-		af9013_statistics_ber_unc_result(&state->fe);
-		state->statistics_step++;
-		next_msec = 100;
-		break;
-	}
-
-	schedule_delayed_work(&state->statistics_work,
-		msecs_to_jiffies(next_msec));
-}
-
 static int af9013_get_tune_settings(struct dvb_frontend *fe,
 	struct dvb_frontend_tune_settings *fesettings)
 {
@@ -751,46 +526,273 @@ static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status)
 {
 	struct af9013_state *state = fe->demodulator_priv;
 	struct i2c_client *client = state->client;
-	int ret;
-	unsigned int utmp;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret, stmp1;
+	unsigned int utmp, utmp1, utmp2, utmp3, utmp4;
+	u8 buf[7];
+
+	dev_dbg(&client->dev, "\n");
 
 	/*
 	 * Return status from the cache if it is younger than 2000ms with the
 	 * exception of last tune is done during 4000ms.
 	 */
-	if (time_is_after_jiffies(
-		state->read_status_jiffies + msecs_to_jiffies(2000)) &&
-		time_is_before_jiffies(
-		state->set_frontend_jiffies + msecs_to_jiffies(4000))
-	) {
-			*status = state->fe_status;
-			return 0;
+	if (time_is_after_jiffies(state->read_status_jiffies + msecs_to_jiffies(2000)) &&
+	    time_is_before_jiffies(state->set_frontend_jiffies + msecs_to_jiffies(4000))) {
+		*status = state->fe_status;
 	} else {
-		*status = 0;
+		/* MPEG2 lock */
+		ret = regmap_read(state->regmap, 0xd507, &utmp);
+		if (ret)
+			goto err;
+
+		if ((utmp >> 6) & 0x01) {
+			utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+				FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+		} else {
+			/* TPS lock */
+			ret = regmap_read(state->regmap, 0xd330, &utmp);
+			if (ret)
+				goto err;
+
+			if ((utmp >> 3) & 0x01)
+				utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+					FE_HAS_VITERBI;
+			else
+				utmp1 = 0;
+		}
+
+		dev_dbg(&client->dev, "fe_status %02x\n", utmp1);
+
+		state->read_status_jiffies = jiffies;
+
+		state->fe_status = utmp1;
+		*status = utmp1;
 	}
 
-	/* MPEG2 lock */
-	ret = regmap_read(state->regmap, 0xd507, &utmp);
-	if (ret)
-		goto err;
+	/* Signal strength */
+	switch (state->strength_en) {
+	case 0:
+		/* Check if we support signal strength */
+		ret = regmap_read(state->regmap, 0x9bee, &utmp);
+		if (ret)
+			goto err;
 
-	if ((utmp >> 6) & 0x01)
-		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
-			FE_HAS_SYNC | FE_HAS_LOCK;
+		if ((utmp >> 0) & 0x01) {
+			/* Read agc values for signal strength estimation */
+			ret = regmap_read(state->regmap, 0x9bbd, &utmp1);
+			if (ret)
+				goto err;
+			ret = regmap_read(state->regmap, 0x9bd0, &utmp2);
+			if (ret)
+				goto err;
+			ret = regmap_read(state->regmap, 0x9be2, &utmp3);
+			if (ret)
+				goto err;
+			ret = regmap_read(state->regmap, 0x9be4, &utmp4);
+			if (ret)
+				goto err;
 
-	if (!*status) {
-		/* TPS lock */
-		ret = regmap_read(state->regmap, 0xd330, &utmp);
+			state->rf_agc_50 = utmp1;
+			state->rf_agc_80 = utmp2;
+			state->if_agc_50 = utmp3;
+			state->if_agc_80 = utmp4;
+			dev_dbg(&client->dev,
+				"rf_agc_50 %u, rf_agc_80 %u, if_agc_50 %u, if_agc_80 %u\n",
+				utmp1, utmp2, utmp3, utmp4);
+
+			state->strength_en = 1;
+		} else {
+			/* Signal strength is not supported */
+			state->strength_en = 2;
+			break;
+		}
+		/* Fall through */
+	case 1:
+		if (time_is_after_jiffies(state->strength_jiffies + msecs_to_jiffies(2000)))
+			break;
+
+		/* Read value */
+		ret = regmap_bulk_read(state->regmap, 0xd07c, buf, 2);
 		if (ret)
 			goto err;
 
-		if ((utmp >> 3) & 0x01)
-			*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
-				FE_HAS_VITERBI;
+		/*
+		 * Construct line equation from tuner dependent -80/-50 dBm agc
+		 * limits and use it to map current agc value to dBm estimate
+		 */
+		#define agc_gain (buf[0] + buf[1])
+		#define agc_gain_50dbm (state->rf_agc_50 + state->if_agc_50)
+		#define agc_gain_80dbm (state->rf_agc_80 + state->if_agc_80)
+		stmp1 = 30000 * (agc_gain - agc_gain_80dbm) /
+			(agc_gain_50dbm - agc_gain_80dbm) - 80000;
+
+		dev_dbg(&client->dev,
+			"strength %d, agc_gain %d, agc_gain_50dbm %d, agc_gain_80dbm %d\n",
+			stmp1, agc_gain, agc_gain_50dbm, agc_gain_80dbm);
+
+		state->strength_jiffies = jiffies;
+		/* Convert [-90, -30] dBm to [0x0000, 0xffff] for dvbv3 */
+		utmp1 = clamp(stmp1 + 90000, 0, 60000);
+		state->dvbv3_strength = div_u64((u64)utmp1 * 0xffff, 60000);
+
+		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+		c->strength.stat[0].svalue = stmp1;
+		break;
+	default:
+		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		break;
 	}
 
-	state->fe_status = *status;
-	state->read_status_jiffies = jiffies;
+	/* CNR */
+	switch (state->fe_status & FE_HAS_VITERBI) {
+	case FE_HAS_VITERBI:
+		if (time_is_after_jiffies(state->cnr_jiffies + msecs_to_jiffies(2000)))
+			break;
+
+		/* Check if cnr ready */
+		ret = regmap_read(state->regmap, 0xd2e1, &utmp);
+		if (ret)
+			goto err;
+
+		if (!((utmp >> 3) & 0x01)) {
+			dev_dbg(&client->dev, "cnr not ready\n");
+			break;
+		}
+
+		/* Read value */
+		ret = regmap_bulk_read(state->regmap, 0xd2e3, buf, 3);
+		if (ret)
+			goto err;
+
+		utmp1 = buf[2] << 16 | buf[1] << 8 | buf[0] << 0;
+
+		/* Read current modulation */
+		ret = regmap_read(state->regmap, 0xd3c1, &utmp);
+		if (ret)
+			goto err;
+
+		switch ((utmp >> 6) & 3) {
+		case 0:
+			/*
+			 * QPSK
+			 * CNR[dB] 13 * -log10((1690000 - value) / value) + 2.6
+			 * value [653799, 1689999], 2.6 / 13 = 3355443
+			 */
+			utmp1 = clamp(utmp1, 653799U, 1689999U);
+			utmp1 = ((u64)(intlog10(utmp1)
+				- intlog10(1690000 - utmp1)
+				+ 3355443) * 13 * 1000) >> 24;
+			break;
+		case 1:
+			/*
+			 * QAM-16
+			 * CNR[dB] 6 * log10((value - 370000) / (828000 - value)) + 15.7
+			 * value [371105, 827999], 15.7 / 6 = 43900382
+			 */
+			utmp1 = clamp(utmp1, 371105U, 827999U);
+			utmp1 = ((u64)(intlog10(utmp1 - 370000)
+				- intlog10(828000 - utmp1)
+				+ 43900382) * 6 * 1000) >> 24;
+			break;
+		case 2:
+			/*
+			 * QAM-64
+			 * CNR[dB] 8 * log10((value - 193000) / (425000 - value)) + 23.8
+			 * value [193246, 424999], 23.8 / 8 = 49912218
+			 */
+			utmp1 = clamp(utmp1, 193246U, 424999U);
+			utmp1 = ((u64)(intlog10(utmp1 - 193000)
+				- intlog10(425000 - utmp1)
+				+ 49912218) * 8 * 1000) >> 24;
+			break;
+		default:
+			dev_dbg(&client->dev, "invalid modulation %u\n",
+				(utmp >> 6) & 3);
+			utmp1 = 0;
+			break;
+		}
+
+		dev_dbg(&client->dev, "cnr %u\n", utmp1);
+
+		state->cnr_jiffies = jiffies;
+		state->dvbv3_snr = utmp1 / 100;
+
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = utmp1;
+		break;
+	default:
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		break;
+	}
+
+	/* BER / PER */
+	switch (state->fe_status & FE_HAS_SYNC) {
+	case FE_HAS_SYNC:
+		if (time_is_after_jiffies(state->ber_ucb_jiffies + msecs_to_jiffies(2000)))
+			break;
+
+		/* Check if ber / ucb is ready */
+		ret = regmap_read(state->regmap, 0xd391, &utmp);
+		if (ret)
+			goto err;
+
+		if (!((utmp >> 4) & 0x01)) {
+			dev_dbg(&client->dev, "ber not ready\n");
+			break;
+		}
+
+		/* Read value */
+		ret = regmap_bulk_read(state->regmap, 0xd385, buf, 7);
+		if (ret)
+			goto err;
+
+		utmp1 = buf[4] << 16 | buf[3] << 8 | buf[2] << 0;
+		utmp2 = (buf[1] << 8 | buf[0] << 0) * 204 * 8;
+		utmp3 = buf[6] << 8 | buf[5] << 0;
+		utmp4 = buf[1] << 8 | buf[0] << 0;
+
+		/* Use 10000 TS packets for measure */
+		if (utmp4 != 10000) {
+			buf[0] = (10000 >> 0) & 0xff;
+			buf[1] = (10000 >> 8) & 0xff;
+			ret = regmap_bulk_write(state->regmap, 0xd385, buf, 2);
+			if (ret)
+				goto err;
+		}
+
+		/* Reset ber / ucb counter */
+		ret = regmap_update_bits(state->regmap, 0xd391, 0x20, 0x20);
+		if (ret)
+			goto err;
+
+		dev_dbg(&client->dev, "post_bit_error %u, post_bit_count %u\n",
+			utmp1, utmp2);
+		dev_dbg(&client->dev, "block_error %u, block_count %u\n",
+			utmp3, utmp4);
+
+		state->ber_ucb_jiffies = jiffies;
+		state->dvbv3_ber = utmp1;
+		state->dvbv3_ucblocks += utmp3;
+
+		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_error.stat[0].uvalue += utmp1;
+		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_count.stat[0].uvalue += utmp2;
+
+		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->block_error.stat[0].uvalue += utmp3;
+		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->block_count.stat[0].uvalue += utmp4;
+		break;
+	default:
+		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		break;
+	}
 
 	return 0;
 err:
@@ -801,28 +803,36 @@ err:
 static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
 	struct af9013_state *state = fe->demodulator_priv;
-	*snr = state->snr;
+
+	*snr = state->dvbv3_snr;
+
 	return 0;
 }
 
 static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
 {
 	struct af9013_state *state = fe->demodulator_priv;
-	*strength = state->signal_strength;
+
+	*strength = state->dvbv3_strength;
+
 	return 0;
 }
 
 static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber)
 {
 	struct af9013_state *state = fe->demodulator_priv;
-	*ber = state->ber;
+
+	*ber = state->dvbv3_ber;
+
 	return 0;
 }
 
 static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 {
 	struct af9013_state *state = fe->demodulator_priv;
-	*ucblocks = state->ucblocks;
+
+	*ucblocks = state->dvbv3_ucblocks;
+
 	return 0;
 }
 
@@ -833,7 +843,7 @@ static int af9013_init(struct dvb_frontend *fe)
 	int ret, i, len;
 	unsigned int utmp;
 	u8 buf[3];
-	const struct af9013_reg_bit *init;
+	const struct af9013_reg_mask_val *tab;
 
 	dev_dbg(&client->dev, "\n");
 
@@ -888,72 +898,66 @@ static int af9013_init(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	/* load OFSM settings */
-	dev_dbg(&client->dev, "load ofsm settings\n");
-	len = ARRAY_SIZE(ofsm_init);
-	init = ofsm_init;
+	/* Demod core settings */
+	dev_dbg(&client->dev, "load demod core settings\n");
+	len = ARRAY_SIZE(demod_init_tab);
+	tab = demod_init_tab;
 	for (i = 0; i < len; i++) {
-		u16 reg = init[i].addr;
-		u8 mask = GENMASK(init[i].pos + init[i].len - 1, init[i].pos);
-		u8 val = init[i].val << init[i].pos;
-
-		ret = regmap_update_bits(state->regmap, reg, mask, val);
+		ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask,
+					 tab[i].val);
 		if (ret)
 			goto err;
 	}
 
-	/* load tuner specific settings */
+	/* Demod tuner specific settings */
 	dev_dbg(&client->dev, "load tuner specific settings\n");
 	switch (state->tuner) {
 	case AF9013_TUNER_MXL5003D:
-		len = ARRAY_SIZE(tuner_init_mxl5003d);
-		init = tuner_init_mxl5003d;
+		len = ARRAY_SIZE(tuner_init_tab_mxl5003d);
+		tab = tuner_init_tab_mxl5003d;
 		break;
 	case AF9013_TUNER_MXL5005D:
 	case AF9013_TUNER_MXL5005R:
 	case AF9013_TUNER_MXL5007T:
-		len = ARRAY_SIZE(tuner_init_mxl5005);
-		init = tuner_init_mxl5005;
+		len = ARRAY_SIZE(tuner_init_tab_mxl5005);
+		tab = tuner_init_tab_mxl5005;
 		break;
 	case AF9013_TUNER_ENV77H11D5:
-		len = ARRAY_SIZE(tuner_init_env77h11d5);
-		init = tuner_init_env77h11d5;
+		len = ARRAY_SIZE(tuner_init_tab_env77h11d5);
+		tab = tuner_init_tab_env77h11d5;
 		break;
 	case AF9013_TUNER_MT2060:
-		len = ARRAY_SIZE(tuner_init_mt2060);
-		init = tuner_init_mt2060;
+		len = ARRAY_SIZE(tuner_init_tab_mt2060);
+		tab = tuner_init_tab_mt2060;
 		break;
 	case AF9013_TUNER_MC44S803:
-		len = ARRAY_SIZE(tuner_init_mc44s803);
-		init = tuner_init_mc44s803;
+		len = ARRAY_SIZE(tuner_init_tab_mc44s803);
+		tab = tuner_init_tab_mc44s803;
 		break;
 	case AF9013_TUNER_QT1010:
 	case AF9013_TUNER_QT1010A:
-		len = ARRAY_SIZE(tuner_init_qt1010);
-		init = tuner_init_qt1010;
+		len = ARRAY_SIZE(tuner_init_tab_qt1010);
+		tab = tuner_init_tab_qt1010;
 		break;
 	case AF9013_TUNER_MT2060_2:
-		len = ARRAY_SIZE(tuner_init_mt2060_2);
-		init = tuner_init_mt2060_2;
+		len = ARRAY_SIZE(tuner_init_tab_mt2060_2);
+		tab = tuner_init_tab_mt2060_2;
 		break;
 	case AF9013_TUNER_TDA18271:
 	case AF9013_TUNER_TDA18218:
-		len = ARRAY_SIZE(tuner_init_tda18271);
-		init = tuner_init_tda18271;
+		len = ARRAY_SIZE(tuner_init_tab_tda18271);
+		tab = tuner_init_tab_tda18271;
 		break;
 	case AF9013_TUNER_UNKNOWN:
 	default:
-		len = ARRAY_SIZE(tuner_init_unknown);
-		init = tuner_init_unknown;
+		len = ARRAY_SIZE(tuner_init_tab_unknown);
+		tab = tuner_init_tab_unknown;
 		break;
 	}
 
 	for (i = 0; i < len; i++) {
-		u16 reg = init[i].addr;
-		u8 mask = GENMASK(init[i].pos + init[i].len - 1, init[i].pos);
-		u8 val = init[i].val << init[i].pos;
-
-		ret = regmap_update_bits(state->regmap, reg, mask, val);
+		ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask,
+					 tab[i].val);
 		if (ret)
 			goto err;
 	}
@@ -972,50 +976,7 @@ static int af9013_init(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	/* check if we support signal strength */
-	if (!state->signal_strength_en) {
-		ret = regmap_read(state->regmap, 0x9bee, &utmp);
-		if (ret)
-			goto err;
-
-		state->signal_strength_en = (utmp >> 0) & 0x01;
-	}
-
-	/* read values needed for signal strength calculation */
-	if (state->signal_strength_en && !state->rf_50) {
-		ret = regmap_bulk_read(state->regmap, 0x9bbd, &state->rf_50, 1);
-		if (ret)
-			goto err;
-		ret = regmap_bulk_read(state->regmap, 0x9bd0, &state->rf_80, 1);
-		if (ret)
-			goto err;
-		ret = regmap_bulk_read(state->regmap, 0x9be2, &state->if_50, 1);
-		if (ret)
-			goto err;
-		ret = regmap_bulk_read(state->regmap, 0x9be4, &state->if_80, 1);
-		if (ret)
-			goto err;
-	}
-
-	/* SNR */
-	ret = regmap_write(state->regmap, 0xd2e2, 0x01);
-	if (ret)
-		goto err;
-
-	/* BER / UCB */
-	buf[0] = (10000 >> 0) & 0xff;
-	buf[1] = (10000 >> 8) & 0xff;
-	ret = regmap_bulk_write(state->regmap, 0xd385, buf, 2);
-	if (ret)
-		goto err;
-
-	/* enable FEC monitor */
-	ret = regmap_update_bits(state->regmap, 0xd392, 0x02, 0x02);
-	if (ret)
-		goto err;
-
 	state->first_tune = true;
-	schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(400));
 
 	return 0;
 err:
@@ -1032,9 +993,6 @@ static int af9013_sleep(struct dvb_frontend *fe)
 
 	dev_dbg(&client->dev, "\n");
 
-	/* stop statistics polling */
-	cancel_delayed_work_sync(&state->statistics_work);
-
 	/* disable lock led */
 	ret = regmap_update_bits(state->regmap, 0xd730, 0x01, 0x00);
 	if (ret)
@@ -1072,45 +1030,6 @@ err:
 	return ret;
 }
 
-static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
-	int ret;
-	struct af9013_state *state = fe->demodulator_priv;
-	struct i2c_client *client = state->client;
-
-	dev_dbg(&client->dev, "enable %d\n", enable);
-
-	/* gate already open or close */
-	if (state->i2c_gate_state == enable)
-		return 0;
-
-	if (state->ts_mode == AF9013_TS_MODE_USB)
-		ret = regmap_update_bits(state->regmap, 0xd417, 0x08,
-					 enable << 3);
-	else
-		ret = regmap_update_bits(state->regmap, 0xd607, 0x04,
-					 enable << 2);
-	if (ret)
-		goto err;
-
-	state->i2c_gate_state = enable;
-
-	return 0;
-err:
-	dev_dbg(&client->dev, "failed %d\n", ret);
-	return ret;
-}
-
-static void af9013_release(struct dvb_frontend *fe)
-{
-	struct af9013_state *state = fe->demodulator_priv;
-	struct i2c_client *client = state->client;
-
-	dev_dbg(&client->dev, "\n");
-
-	i2c_unregister_device(client);
-}
-
 static const struct dvb_frontend_ops af9013_ops;
 
 static int af9013_download_firmware(struct af9013_state *state)
@@ -1213,40 +1132,6 @@ err:
 	return ret;
 }
 
-/*
- * XXX: That is wrapper to af9013_probe() via driver core in order to provide
- * proper I2C client for legacy media attach binding.
- * New users must use I2C client binding directly!
- */
-struct dvb_frontend *af9013_attach(const struct af9013_config *config,
-				   struct i2c_adapter *i2c)
-{
-	struct i2c_client *client;
-	struct i2c_board_info board_info;
-	struct af9013_platform_data pdata;
-
-	pdata.clk = config->clock;
-	pdata.tuner = config->tuner;
-	pdata.if_frequency = config->if_frequency;
-	pdata.ts_mode = config->ts_mode;
-	pdata.ts_output_pin = 7;
-	pdata.spec_inv = config->spec_inv;
-	memcpy(&pdata.api_version, config->api_version, sizeof(pdata.api_version));
-	memcpy(&pdata.gpio, config->gpio, sizeof(pdata.gpio));
-	pdata.attach_in_use = true;
-
-	memset(&board_info, 0, sizeof(board_info));
-	strlcpy(board_info.type, "af9013", sizeof(board_info.type));
-	board_info.addr = config->i2c_addr;
-	board_info.platform_data = &pdata;
-	client = i2c_new_device(i2c, &board_info);
-	if (!client || !client->dev.driver)
-		return NULL;
-
-	return pdata.get_dvb_frontend(client);
-}
-EXPORT_SYMBOL(af9013_attach);
-
 static const struct dvb_frontend_ops af9013_ops = {
 	.delsys = { SYS_DVBT },
 	.info = {
@@ -1272,8 +1157,6 @@ static const struct dvb_frontend_ops af9013_ops = {
 			FE_CAN_MUTE_TS
 	},
 
-	.release = af9013_release,
-
 	.init = af9013_init,
 	.sleep = af9013_sleep,
 
@@ -1286,10 +1169,58 @@ static const struct dvb_frontend_ops af9013_ops = {
 	.read_signal_strength = af9013_read_signal_strength,
 	.read_ber = af9013_read_ber,
 	.read_ucblocks = af9013_read_ucblocks,
-
-	.i2c_gate_ctrl = af9013_i2c_gate_ctrl,
 };
 
+static int af9013_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	struct i2c_client *client = state->client;
+	int ret;
+
+	dev_dbg(&client->dev, "onoff %d\n", onoff);
+
+	ret = regmap_update_bits(state->regmap, 0xd503, 0x01, onoff);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed %d\n", ret);
+	return ret;
+}
+
+static int af9013_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
+			     int onoff)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	struct i2c_client *client = state->client;
+	int ret;
+	u8 buf[2];
+
+	dev_dbg(&client->dev, "index %d, pid %04x, onoff %d\n",
+		index, pid, onoff);
+
+	if (pid > 0x1fff) {
+		/* 0x2000 is kernel virtual pid for whole ts (all pids) */
+		ret = 0;
+		goto err;
+	}
+
+	buf[0] = (pid >> 0) & 0xff;
+	buf[1] = (pid >> 8) & 0xff;
+	ret = regmap_bulk_write(state->regmap, 0xd505, buf, 2);
+	if (ret)
+		goto err;
+	ret = regmap_write(state->regmap, 0xd504, onoff << 5 | index << 0);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed %d\n", ret);
+	return ret;
+}
+
 static struct dvb_frontend *af9013_get_dvb_frontend(struct i2c_client *client)
 {
 	struct af9013_state *state = i2c_get_clientdata(client);
@@ -1299,9 +1230,65 @@ static struct dvb_frontend *af9013_get_dvb_frontend(struct i2c_client *client)
 	return &state->fe;
 }
 
+static struct i2c_adapter *af9013_get_i2c_adapter(struct i2c_client *client)
+{
+	struct af9013_state *state = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "\n");
+
+	return state->muxc->adapter[0];
+}
+
+/*
+ * XXX: Hackish solution. We use virtual register, reg bit 16, to carry info
+ * about i2c adapter locking. Own locking is needed because i2c mux call has
+ * already locked i2c adapter.
+ */
+static int af9013_select(struct i2c_mux_core *muxc, u32 chan)
+{
+	struct af9013_state *state = i2c_mux_priv(muxc);
+	struct i2c_client *client = state->client;
+	int ret;
+
+	dev_dbg(&client->dev, "\n");
+
+	if (state->ts_mode == AF9013_TS_MODE_USB)
+		ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x08);
+	else
+		ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x04);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed %d\n", ret);
+	return ret;
+}
+
+static int af9013_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+	struct af9013_state *state = i2c_mux_priv(muxc);
+	struct i2c_client *client = state->client;
+	int ret;
+
+	dev_dbg(&client->dev, "\n");
+
+	if (state->ts_mode == AF9013_TS_MODE_USB)
+		ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x00);
+	else
+		ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x00);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed %d\n", ret);
+	return ret;
+}
+
 /* Own I2C access routines needed for regmap as chip uses extra command byte */
 static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg,
-			const u8 *val, int len)
+			const u8 *val, int len, u8 lock)
 {
 	int ret;
 	u8 buf[21];
@@ -1323,7 +1310,12 @@ static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg,
 	buf[1] = (reg >> 0) & 0xff;
 	buf[2] = cmd;
 	memcpy(&buf[3], val, len);
-	ret = i2c_transfer(client->adapter, msg, 1);
+
+	if (lock)
+		i2c_lock_adapter(client->adapter);
+	ret = __i2c_transfer(client->adapter, msg, 1);
+	if (lock)
+		i2c_unlock_adapter(client->adapter);
 	if (ret < 0) {
 		goto err;
 	} else if (ret != 1) {
@@ -1338,7 +1330,7 @@ err:
 }
 
 static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg,
-			u8 *val, int len)
+			u8 *val, int len, u8 lock)
 {
 	int ret;
 	u8 buf[3];
@@ -1359,7 +1351,12 @@ static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg,
 	buf[0] = (reg >> 8) & 0xff;
 	buf[1] = (reg >> 0) & 0xff;
 	buf[2] = cmd;
-	ret = i2c_transfer(client->adapter, msg, 2);
+
+	if (lock)
+		i2c_lock_adapter(client->adapter);
+	ret = __i2c_transfer(client->adapter, msg, 2);
+	if (lock)
+		i2c_unlock_adapter(client->adapter);
 	if (ret < 0) {
 		goto err;
 	} else if (ret != 2) {
@@ -1379,25 +1376,27 @@ static int af9013_regmap_write(void *context, const void *data, size_t count)
 	struct af9013_state *state = i2c_get_clientdata(client);
 	int ret, i;
 	u8 cmd;
-	u16 reg = ((u8 *)data)[0] << 8|((u8 *)data)[1] << 0;
-	u8 *val = &((u8 *)data)[2];
-	const unsigned int len = count - 2;
+	u8 lock = !((u8 *)data)[0];
+	u16 reg = ((u8 *)data)[1] << 8 | ((u8 *)data)[2] << 0;
+	u8 *val = &((u8 *)data)[3];
+	const unsigned int len = count - 3;
 
 	if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) {
 		cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|1 << 0;
-		ret = af9013_wregs(client, cmd, reg, val, len);
+		ret = af9013_wregs(client, cmd, reg, val, len, lock);
 		if (ret)
 			goto err;
 	} else if (reg >= 0x5100 && reg < 0x8fff) {
 		/* Firmware download */
 		cmd = 1 << 7|1 << 6|(len - 1) << 2|1 << 1|1 << 0;
-		ret = af9013_wregs(client, cmd, reg, val, len);
+		ret = af9013_wregs(client, cmd, reg, val, len, lock);
 		if (ret)
 			goto err;
 	} else {
 		cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|1 << 0;
 		for (i = 0; i < len; i++) {
-			ret = af9013_wregs(client, cmd, reg + i, val + i, 1);
+			ret = af9013_wregs(client, cmd, reg + i, val + i, 1,
+					   lock);
 			if (ret)
 				goto err;
 		}
@@ -1416,19 +1415,21 @@ static int af9013_regmap_read(void *context, const void *reg_buf,
 	struct af9013_state *state = i2c_get_clientdata(client);
 	int ret, i;
 	u8 cmd;
-	u16 reg = ((u8 *)reg_buf)[0] << 8|((u8 *)reg_buf)[1] << 0;
+	u8 lock = !((u8 *)reg_buf)[0];
+	u16 reg = ((u8 *)reg_buf)[1] << 8 | ((u8 *)reg_buf)[2] << 0;
 	u8 *val = &((u8 *)val_buf)[0];
 	const unsigned int len = val_size;
 
 	if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) {
 		cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|0 << 0;
-		ret = af9013_rregs(client, cmd, reg, val_buf, len);
+		ret = af9013_rregs(client, cmd, reg, val_buf, len, lock);
 		if (ret)
 			goto err;
 	} else {
 		cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|0 << 0;
 		for (i = 0; i < len; i++) {
-			ret = af9013_rregs(client, cmd, reg + i, val + i, 1);
+			ret = af9013_rregs(client, cmd, reg + i, val + i, 1,
+					   lock);
 			if (ret)
 				goto err;
 		}
@@ -1453,8 +1454,9 @@ static int af9013_probe(struct i2c_client *client,
 		.write = af9013_regmap_write,
 	};
 	static const struct regmap_config regmap_config = {
-		.reg_bits    =  16,
-		.val_bits    =  8,
+		/* Actual reg is 16 bits, see i2c adapter lock */
+		.reg_bits = 24,
+		.val_bits = 8,
 	};
 
 	state = kzalloc(sizeof(*state), GFP_KERNEL);
@@ -1463,6 +1465,8 @@ static int af9013_probe(struct i2c_client *client,
 		goto err;
 	}
 
+	dev_dbg(&client->dev, "\n");
+
 	/* Setup the state */
 	state->client = client;
 	i2c_set_clientdata(client, state);
@@ -1474,52 +1478,70 @@ static int af9013_probe(struct i2c_client *client,
 	state->spec_inv = pdata->spec_inv;
 	memcpy(&state->api_version, pdata->api_version, sizeof(state->api_version));
 	memcpy(&state->gpio, pdata->gpio, sizeof(state->gpio));
-	INIT_DELAYED_WORK(&state->statistics_work, af9013_statistics_work);
 	state->regmap = regmap_init(&client->dev, &regmap_bus, client,
 				  &regmap_config);
 	if (IS_ERR(state->regmap)) {
 		ret = PTR_ERR(state->regmap);
 		goto err_kfree;
 	}
+	/* Create mux i2c adapter */
+	state->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
+				    af9013_select, af9013_deselect);
+	if (!state->muxc) {
+		ret = -ENOMEM;
+		goto err_regmap_exit;
+	}
+	state->muxc->priv = state;
+	ret = i2c_mux_add_adapter(state->muxc, 0, 0, 0);
+	if (ret)
+		goto err_regmap_exit;
 
 	/* Download firmware */
 	if (state->ts_mode != AF9013_TS_MODE_USB) {
 		ret = af9013_download_firmware(state);
 		if (ret)
-			goto err_regmap_exit;
+			goto err_i2c_mux_del_adapters;
 	}
 
 	/* Firmware version */
 	ret = regmap_bulk_read(state->regmap, 0x5103, firmware_version,
 			       sizeof(firmware_version));
 	if (ret)
-		goto err_regmap_exit;
+		goto err_i2c_mux_del_adapters;
 
 	/* Set GPIOs */
 	for (i = 0; i < sizeof(state->gpio); i++) {
 		ret = af9013_set_gpio(state, i, state->gpio[i]);
 		if (ret)
-			goto err_regmap_exit;
+			goto err_i2c_mux_del_adapters;
 	}
 
 	/* Create dvb frontend */
 	memcpy(&state->fe.ops, &af9013_ops, sizeof(state->fe.ops));
-	if (!pdata->attach_in_use)
-		state->fe.ops.release = NULL;
 	state->fe.demodulator_priv = state;
 
 	/* Setup callbacks */
 	pdata->get_dvb_frontend = af9013_get_dvb_frontend;
+	pdata->get_i2c_adapter = af9013_get_i2c_adapter;
+	pdata->pid_filter = af9013_pid_filter;
+	pdata->pid_filter_ctrl = af9013_pid_filter_ctrl;
 
 	/* Init stats to indicate which stats are supported */
 	c = &state->fe.dtv_property_cache;
+	c->strength.len = 1;
 	c->cnr.len = 1;
+	c->post_bit_error.len = 1;
+	c->post_bit_count.len = 1;
+	c->block_error.len = 1;
+	c->block_count.len = 1;
 
 	dev_info(&client->dev, "Afatech AF9013 successfully attached\n");
 	dev_info(&client->dev, "firmware version: %d.%d.%d.%d\n",
 		 firmware_version[0], firmware_version[1],
 		 firmware_version[2], firmware_version[3]);
 	return 0;
+err_i2c_mux_del_adapters:
+	i2c_mux_del_adapters(state->muxc);
 err_regmap_exit:
 	regmap_exit(state->regmap);
 err_kfree:
@@ -1535,8 +1557,7 @@ static int af9013_remove(struct i2c_client *client)
 
 	dev_dbg(&client->dev, "\n");
 
-	/* Stop statistics polling */
-	cancel_delayed_work_sync(&state->statistics_work);
+	i2c_mux_del_adapters(state->muxc);
 
 	regmap_exit(state->regmap);
 

+ 13 - 35
drivers/media/dvb-frontends/af9013.h

@@ -38,13 +38,9 @@
  * @api_version: Firmware API version.
  * @gpio: GPIOs.
  * @get_dvb_frontend: Get DVB frontend callback.
- *
- * AF9013/5 GPIOs (mostly guessed):
- *   * demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
- *   * demod#1-gpio#1 - xtal setting (?)
- *   * demod#1-gpio#3 - tuner#1
- *   * demod#2-gpio#0 - tuner#2
- *   * demod#2-gpio#1 - xtal setting (?)
+ * @get_i2c_adapter: Get I2C adapter.
+ * @pid_filter_ctrl: Control PID filter.
+ * @pid_filter: Set PID to PID filter.
  */
 struct af9013_platform_data {
 	/*
@@ -84,36 +80,18 @@ struct af9013_platform_data {
 	u8 gpio[4];
 
 	struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
-
-/* private: For legacy media attach wrapper. Do not set value. */
-	bool attach_in_use;
-	u8 i2c_addr;
-	u32 clock;
+	struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *);
+	int (*pid_filter_ctrl)(struct dvb_frontend *, int);
+	int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
 };
 
-#define af9013_config       af9013_platform_data
-#define AF9013_TS_USB       AF9013_TS_MODE_USB
-#define AF9013_TS_PARALLEL  AF9013_TS_MODE_PARALLEL
-#define AF9013_TS_SERIAL    AF9013_TS_MODE_SERIAL
-
-#if IS_REACHABLE(CONFIG_DVB_AF9013)
-/**
- * Attach an af9013 demod
- *
- * @config: pointer to &struct af9013_config with demod configuration.
- * @i2c: i2c adapter to use.
- *
- * return: FE pointer on success, NULL on failure.
+/*
+ * AF9013/5 GPIOs (mostly guessed)
+ * demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
+ * demod#1-gpio#1 - xtal setting (?)
+ * demod#1-gpio#3 - tuner#1
+ * demod#2-gpio#0 - tuner#2
+ * demod#2-gpio#1 - xtal setting (?)
  */
-extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
-	struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend *af9013_attach(
-const struct af9013_config *config, struct i2c_adapter *i2c)
-{
-	pr_warn("%s: driver disabled by Kconfig\n", __func__);
-	return NULL;
-}
-#endif /* CONFIG_DVB_AF9013 */
 
 #endif /* AF9013_H */

+ 756 - 802
drivers/media/dvb-frontends/af9013_priv.h

@@ -22,25 +22,21 @@
 #define AF9013_PRIV_H
 
 #include <media/dvb_frontend.h>
+#include <media/dvb_math.h>
 #include "af9013.h"
 #include <linux/firmware.h>
+#include <linux/i2c-mux.h>
 #include <linux/math64.h>
 #include <linux/regmap.h>
 
 #define AF9013_FIRMWARE "dvb-fe-af9013.fw"
 
-struct af9013_reg_bit {
-	u16 addr;
-	u8  pos:4;
-	u8  len:4;
+struct af9013_reg_mask_val {
+	u16 reg;
+	u8  mask;
 	u8  val;
 };
 
-struct af9013_snr {
-	u32 val;
-	u8 snr;
-};
-
 struct af9013_coeff {
 	u32 clock;
 	u32 bandwidth_hz;
@@ -91,817 +87,775 @@ static const struct af9013_coeff coeff_lut[] = {
 		0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } },
 };
 
-/* QPSK SNR lookup table */
-static const struct af9013_snr qpsk_snr_lut[] = {
-	{ 0x000000,  0 },
-	{ 0x0b4771,  0 },
-	{ 0x0c1aed,  1 },
-	{ 0x0d0d27,  2 },
-	{ 0x0e4d19,  3 },
-	{ 0x0e5da8,  4 },
-	{ 0x107097,  5 },
-	{ 0x116975,  6 },
-	{ 0x1252d9,  7 },
-	{ 0x131fa4,  8 },
-	{ 0x13d5e1,  9 },
-	{ 0x148e53, 10 },
-	{ 0x15358b, 11 },
-	{ 0x15dd29, 12 },
-	{ 0x168112, 13 },
-	{ 0x170b61, 14 },
-	{ 0xffffff, 15 },
-};
-
-/* QAM16 SNR lookup table */
-static const struct af9013_snr qam16_snr_lut[] = {
-	{ 0x000000,  0 },
-	{ 0x05eb62,  5 },
-	{ 0x05fecf,  6 },
-	{ 0x060b80,  7 },
-	{ 0x062501,  8 },
-	{ 0x064865,  9 },
-	{ 0x069604, 10 },
-	{ 0x06f356, 11 },
-	{ 0x07706a, 12 },
-	{ 0x0804d3, 13 },
-	{ 0x089d1a, 14 },
-	{ 0x093e3d, 15 },
-	{ 0x09e35d, 16 },
-	{ 0x0a7c3c, 17 },
-	{ 0x0afaf8, 18 },
-	{ 0x0b719d, 19 },
-	{ 0xffffff, 20 },
-};
-
-/* QAM64 SNR lookup table */
-static const struct af9013_snr qam64_snr_lut[] = {
-	{ 0x000000,  0 },
-	{ 0x03109b, 12 },
-	{ 0x0310d4, 13 },
-	{ 0x031920, 14 },
-	{ 0x0322d0, 15 },
-	{ 0x0339fc, 16 },
-	{ 0x0364a1, 17 },
-	{ 0x038bcc, 18 },
-	{ 0x03c7d3, 19 },
-	{ 0x0408cc, 20 },
-	{ 0x043bed, 21 },
-	{ 0x048061, 22 },
-	{ 0x04be95, 23 },
-	{ 0x04fa7d, 24 },
-	{ 0x052405, 25 },
-	{ 0x05570d, 26 },
-	{ 0xffffff, 27 },
-};
-
-static const struct af9013_reg_bit ofsm_init[] = {
-	{ 0xd73a, 0, 8, 0xa1 },
-	{ 0xd73b, 0, 8, 0x1f },
-	{ 0xd73c, 4, 4, 0x0a },
-	{ 0xd732, 3, 1, 0x00 },
-	{ 0xd731, 4, 2, 0x03 },
-	{ 0xd73d, 7, 1, 0x01 },
-	{ 0xd740, 0, 1, 0x00 },
-	{ 0xd740, 1, 1, 0x00 },
-	{ 0xd740, 2, 1, 0x00 },
-	{ 0xd740, 3, 1, 0x01 },
-	{ 0xd3c1, 4, 1, 0x01 },
-	{ 0x9124, 0, 8, 0x58 },
-	{ 0x9125, 0, 2, 0x02 },
-	{ 0xd3a2, 0, 8, 0x00 },
-	{ 0xd3a3, 0, 8, 0x04 },
-	{ 0xd305, 0, 8, 0x32 },
-	{ 0xd306, 0, 8, 0x10 },
-	{ 0xd304, 0, 8, 0x04 },
-	{ 0x9112, 0, 1, 0x01 },
-	{ 0x911d, 0, 1, 0x01 },
-	{ 0x911a, 0, 1, 0x01 },
-	{ 0x911b, 0, 1, 0x01 },
-	{ 0x9bce, 0, 4, 0x02 },
-	{ 0x9116, 0, 1, 0x01 },
-	{ 0x9122, 0, 8, 0xd0 },
-	{ 0xd2e0, 0, 8, 0xd0 },
-	{ 0xd2e9, 0, 4, 0x0d },
-	{ 0xd38c, 0, 8, 0xfc },
-	{ 0xd38d, 0, 8, 0x00 },
-	{ 0xd38e, 0, 8, 0x7e },
-	{ 0xd38f, 0, 8, 0x00 },
-	{ 0xd390, 0, 8, 0x2f },
-	{ 0xd145, 4, 1, 0x01 },
-	{ 0xd1a9, 4, 1, 0x01 },
-	{ 0xd158, 5, 3, 0x01 },
-	{ 0xd159, 0, 6, 0x06 },
-	{ 0xd167, 0, 8, 0x00 },
-	{ 0xd168, 0, 4, 0x07 },
-	{ 0xd1c3, 5, 3, 0x00 },
-	{ 0xd1c4, 0, 6, 0x00 },
-	{ 0xd1c5, 0, 7, 0x10 },
-	{ 0xd1c6, 0, 3, 0x02 },
-	{ 0xd080, 2, 5, 0x03 },
-	{ 0xd081, 4, 4, 0x09 },
-	{ 0xd098, 4, 4, 0x0f },
-	{ 0xd098, 0, 4, 0x03 },
-	{ 0xdbc0, 4, 1, 0x01 },
-	{ 0xdbc7, 0, 8, 0x08 },
-	{ 0xdbc8, 4, 4, 0x00 },
-	{ 0xdbc9, 0, 5, 0x01 },
-	{ 0xd280, 0, 8, 0xe0 },
-	{ 0xd281, 0, 8, 0xff },
-	{ 0xd282, 0, 8, 0xff },
-	{ 0xd283, 0, 8, 0xc3 },
-	{ 0xd284, 0, 8, 0xff },
-	{ 0xd285, 0, 4, 0x01 },
-	{ 0xd0f0, 0, 7, 0x1a },
-	{ 0xd0f1, 4, 1, 0x01 },
-	{ 0xd0f2, 0, 8, 0x0c },
-	{ 0xd101, 5, 3, 0x06 },
-	{ 0xd103, 0, 4, 0x08 },
-	{ 0xd0f8, 0, 7, 0x20 },
-	{ 0xd111, 5, 1, 0x00 },
-	{ 0xd111, 6, 1, 0x00 },
-	{ 0x910b, 0, 8, 0x0a },
-	{ 0x9115, 0, 8, 0x02 },
-	{ 0x910c, 0, 8, 0x02 },
-	{ 0x910d, 0, 8, 0x08 },
-	{ 0x910e, 0, 8, 0x0a },
-	{ 0x9bf6, 0, 8, 0x06 },
-	{ 0x9bf8, 0, 8, 0x02 },
-	{ 0x9bf7, 0, 8, 0x05 },
-	{ 0x9bf9, 0, 8, 0x0f },
-	{ 0x9bfc, 0, 8, 0x13 },
-	{ 0x9bd3, 0, 8, 0xff },
-	{ 0x9bbe, 0, 1, 0x01 },
-	{ 0x9bcc, 0, 1, 0x01 },
+/*
+ * Afatech AF9013 demod init
+ */
+static const struct af9013_reg_mask_val demod_init_tab[] = {
+	{0xd73a, 0xff, 0xa1},
+	{0xd73b, 0xff, 0x1f},
+	{0xd73c, 0xf0, 0xa0},
+	{0xd732, 0x08, 0x00},
+	{0xd731, 0x30, 0x30},
+	{0xd73d, 0x80, 0x80},
+	{0xd740, 0x01, 0x00},
+	{0xd740, 0x02, 0x00},
+	{0xd740, 0x04, 0x00},
+	{0xd740, 0x08, 0x08},
+	{0xd3c1, 0x10, 0x10},
+	{0x9124, 0xff, 0x58},
+	{0x9125, 0x03, 0x02},
+	{0xd3a2, 0xff, 0x00},
+	{0xd3a3, 0xff, 0x04},
+	{0xd305, 0xff, 0x32},
+	{0xd306, 0xff, 0x10},
+	{0xd304, 0xff, 0x04},
+	{0x9112, 0x01, 0x01},
+	{0x911d, 0x01, 0x01},
+	{0x911a, 0x01, 0x01},
+	{0x911b, 0x01, 0x01},
+	{0x9bce, 0x0f, 0x02},
+	{0x9116, 0x01, 0x01},
+	{0x9122, 0xff, 0xd0},
+	{0xd2e0, 0xff, 0xd0},
+	{0xd2e9, 0x0f, 0x0d},
+	{0xd38c, 0xff, 0xfc},
+	{0xd38d, 0xff, 0x00},
+	{0xd38e, 0xff, 0x7e},
+	{0xd38f, 0xff, 0x00},
+	{0xd390, 0xff, 0x2f},
+	{0xd145, 0x10, 0x10},
+	{0xd1a9, 0x10, 0x10},
+	{0xd158, 0xe0, 0x20},
+	{0xd159, 0x3f, 0x06},
+	{0xd167, 0xff, 0x00},
+	{0xd168, 0x0f, 0x07},
+	{0xd1c3, 0xe0, 0x00},
+	{0xd1c4, 0x3f, 0x00},
+	{0xd1c5, 0x7f, 0x10},
+	{0xd1c6, 0x07, 0x02},
+	{0xd080, 0x7c, 0x0c},
+	{0xd081, 0xf0, 0x90},
+	{0xd098, 0xf0, 0xf0},
+	{0xd098, 0x0f, 0x03},
+	{0xdbc0, 0x10, 0x10},
+	{0xdbc7, 0xff, 0x08},
+	{0xdbc8, 0xf0, 0x00},
+	{0xdbc9, 0x1f, 0x01},
+	{0xd280, 0xff, 0xe0},
+	{0xd281, 0xff, 0xff},
+	{0xd282, 0xff, 0xff},
+	{0xd283, 0xff, 0xc3},
+	{0xd284, 0xff, 0xff},
+	{0xd285, 0x0f, 0x01},
+	{0xd0f0, 0x7f, 0x1a},
+	{0xd0f1, 0x10, 0x10},
+	{0xd0f2, 0xff, 0x0c},
+	{0xd101, 0xe0, 0xc0},
+	{0xd103, 0x0f, 0x08},
+	{0xd0f8, 0x7f, 0x20},
+	{0xd111, 0x20, 0x00},
+	{0xd111, 0x40, 0x00},
+	{0x910b, 0xff, 0x0a},
+	{0x9115, 0xff, 0x02},
+	{0x910c, 0xff, 0x02},
+	{0x910d, 0xff, 0x08},
+	{0x910e, 0xff, 0x0a},
+	{0x9bf6, 0xff, 0x06},
+	{0x9bf8, 0xff, 0x02},
+	{0x9bf7, 0xff, 0x05},
+	{0x9bf9, 0xff, 0x0f},
+	{0x9bfc, 0xff, 0x13},
+	{0x9bd3, 0xff, 0xff},
+	{0x9bbe, 0x01, 0x01},
+	{0x9bcc, 0x01, 0x01},
 };
 
-/* Panasonic ENV77H11D5 tuner init
-   AF9013_TUNER_ENV77H11D5 = 129 */
-static const struct af9013_reg_bit tuner_init_env77h11d5[] = {
-	{ 0x9bd5, 0, 8, 0x01 },
-	{ 0x9bd6, 0, 8, 0x03 },
-	{ 0x9bbe, 0, 8, 0x01 },
-	{ 0xd1a0, 1, 1, 0x01 },
-	{ 0xd000, 0, 1, 0x01 },
-	{ 0xd000, 1, 1, 0x00 },
-	{ 0xd001, 1, 1, 0x01 },
-	{ 0xd001, 0, 1, 0x00 },
-	{ 0xd001, 5, 1, 0x00 },
-	{ 0xd002, 0, 5, 0x19 },
-	{ 0xd003, 0, 5, 0x1a },
-	{ 0xd004, 0, 5, 0x19 },
-	{ 0xd005, 0, 5, 0x1a },
-	{ 0xd00e, 0, 5, 0x10 },
-	{ 0xd00f, 0, 3, 0x04 },
-	{ 0xd00f, 3, 3, 0x05 },
-	{ 0xd010, 0, 3, 0x04 },
-	{ 0xd010, 3, 3, 0x05 },
-	{ 0xd016, 4, 4, 0x03 },
-	{ 0xd01f, 0, 6, 0x0a },
-	{ 0xd020, 0, 6, 0x0a },
-	{ 0x9bda, 0, 8, 0x00 },
-	{ 0x9be3, 0, 8, 0x00 },
-	{ 0xd015, 0, 8, 0x50 },
-	{ 0xd016, 0, 1, 0x00 },
-	{ 0xd044, 0, 8, 0x46 },
-	{ 0xd045, 0, 1, 0x00 },
-	{ 0xd008, 0, 8, 0xdf },
-	{ 0xd009, 0, 2, 0x02 },
-	{ 0xd006, 0, 8, 0x44 },
-	{ 0xd007, 0, 2, 0x01 },
-	{ 0xd00c, 0, 8, 0xeb },
-	{ 0xd00d, 0, 2, 0x02 },
-	{ 0xd00a, 0, 8, 0xf4 },
-	{ 0xd00b, 0, 2, 0x01 },
-	{ 0x9bba, 0, 8, 0xf9 },
-	{ 0x9bc3, 0, 8, 0xdf },
-	{ 0x9bc4, 0, 8, 0x02 },
-	{ 0x9bc5, 0, 8, 0xeb },
-	{ 0x9bc6, 0, 8, 0x02 },
-	{ 0x9bc9, 0, 8, 0x52 },
-	{ 0xd011, 0, 8, 0x3c },
-	{ 0xd012, 0, 2, 0x01 },
-	{ 0xd013, 0, 8, 0xf7 },
-	{ 0xd014, 0, 2, 0x02 },
-	{ 0xd040, 0, 8, 0x0b },
-	{ 0xd041, 0, 2, 0x02 },
-	{ 0xd042, 0, 8, 0x4d },
-	{ 0xd043, 0, 2, 0x00 },
-	{ 0xd045, 1, 1, 0x00 },
-	{ 0x9bcf, 0, 1, 0x01 },
-	{ 0xd045, 2, 1, 0x01 },
-	{ 0xd04f, 0, 8, 0x9a },
-	{ 0xd050, 0, 1, 0x01 },
-	{ 0xd051, 0, 8, 0x5a },
-	{ 0xd052, 0, 1, 0x01 },
-	{ 0xd053, 0, 8, 0x50 },
-	{ 0xd054, 0, 8, 0x46 },
-	{ 0x9bd7, 0, 8, 0x0a },
-	{ 0x9bd8, 0, 8, 0x14 },
-	{ 0x9bd9, 0, 8, 0x08 },
+/*
+ * Panasonic ENV77H11D5 tuner init
+ * AF9013_TUNER_ENV77H11D5    0x81
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_env77h11d5[] = {
+	{0x9bd5, 0xff, 0x01},
+	{0x9bd6, 0xff, 0x03},
+	{0x9bbe, 0xff, 0x01},
+	{0xd1a0, 0x02, 0x02},
+	{0xd000, 0x01, 0x01},
+	{0xd000, 0x02, 0x00},
+	{0xd001, 0x02, 0x02},
+	{0xd001, 0x01, 0x00},
+	{0xd001, 0x20, 0x00},
+	{0xd002, 0x1f, 0x19},
+	{0xd003, 0x1f, 0x1a},
+	{0xd004, 0x1f, 0x19},
+	{0xd005, 0x1f, 0x1a},
+	{0xd00e, 0x1f, 0x10},
+	{0xd00f, 0x07, 0x04},
+	{0xd00f, 0x38, 0x28},
+	{0xd010, 0x07, 0x04},
+	{0xd010, 0x38, 0x28},
+	{0xd016, 0xf0, 0x30},
+	{0xd01f, 0x3f, 0x0a},
+	{0xd020, 0x3f, 0x0a},
+	{0x9bda, 0xff, 0x00},
+	{0x9be3, 0xff, 0x00},
+	{0xd015, 0xff, 0x50},
+	{0xd016, 0x01, 0x00},
+	{0xd044, 0xff, 0x46},
+	{0xd045, 0x01, 0x00},
+	{0xd008, 0xff, 0xdf},
+	{0xd009, 0x03, 0x02},
+	{0xd006, 0xff, 0x44},
+	{0xd007, 0x03, 0x01},
+	{0xd00c, 0xff, 0xeb},
+	{0xd00d, 0x03, 0x02},
+	{0xd00a, 0xff, 0xf4},
+	{0xd00b, 0x03, 0x01},
+	{0x9bba, 0xff, 0xf9},
+	{0x9bc3, 0xff, 0xdf},
+	{0x9bc4, 0xff, 0x02},
+	{0x9bc5, 0xff, 0xeb},
+	{0x9bc6, 0xff, 0x02},
+	{0x9bc9, 0xff, 0x52},
+	{0xd011, 0xff, 0x3c},
+	{0xd012, 0x03, 0x01},
+	{0xd013, 0xff, 0xf7},
+	{0xd014, 0x03, 0x02},
+	{0xd040, 0xff, 0x0b},
+	{0xd041, 0x03, 0x02},
+	{0xd042, 0xff, 0x4d},
+	{0xd043, 0x03, 0x00},
+	{0xd045, 0x02, 0x00},
+	{0x9bcf, 0x01, 0x01},
+	{0xd045, 0x04, 0x04},
+	{0xd04f, 0xff, 0x9a},
+	{0xd050, 0x01, 0x01},
+	{0xd051, 0xff, 0x5a},
+	{0xd052, 0x01, 0x01},
+	{0xd053, 0xff, 0x50},
+	{0xd054, 0xff, 0x46},
+	{0x9bd7, 0xff, 0x0a},
+	{0x9bd8, 0xff, 0x14},
+	{0x9bd9, 0xff, 0x08},
 };
 
-/* Microtune MT2060 tuner init
-   AF9013_TUNER_MT2060     = 130 */
-static const struct af9013_reg_bit tuner_init_mt2060[] = {
-	{ 0x9bd5, 0, 8, 0x01 },
-	{ 0x9bd6, 0, 8, 0x07 },
-	{ 0xd1a0, 1, 1, 0x01 },
-	{ 0xd000, 0, 1, 0x01 },
-	{ 0xd000, 1, 1, 0x00 },
-	{ 0xd001, 1, 1, 0x01 },
-	{ 0xd001, 0, 1, 0x00 },
-	{ 0xd001, 5, 1, 0x00 },
-	{ 0xd002, 0, 5, 0x19 },
-	{ 0xd003, 0, 5, 0x1a },
-	{ 0xd004, 0, 5, 0x19 },
-	{ 0xd005, 0, 5, 0x1a },
-	{ 0xd00e, 0, 5, 0x10 },
-	{ 0xd00f, 0, 3, 0x04 },
-	{ 0xd00f, 3, 3, 0x05 },
-	{ 0xd010, 0, 3, 0x04 },
-	{ 0xd010, 3, 3, 0x05 },
-	{ 0xd016, 4, 4, 0x03 },
-	{ 0xd01f, 0, 6, 0x0a },
-	{ 0xd020, 0, 6, 0x0a },
-	{ 0x9bda, 0, 8, 0x00 },
-	{ 0x9be3, 0, 8, 0x00 },
-	{ 0x9bbe, 0, 1, 0x00 },
-	{ 0x9bcc, 0, 1, 0x00 },
-	{ 0x9bb9, 0, 8, 0x75 },
-	{ 0x9bcd, 0, 8, 0x24 },
-	{ 0x9bff, 0, 8, 0x30 },
-	{ 0xd015, 0, 8, 0x46 },
-	{ 0xd016, 0, 1, 0x00 },
-	{ 0xd044, 0, 8, 0x46 },
-	{ 0xd045, 0, 1, 0x00 },
-	{ 0xd008, 0, 8, 0x0f },
-	{ 0xd009, 0, 2, 0x02 },
-	{ 0xd006, 0, 8, 0x32 },
-	{ 0xd007, 0, 2, 0x01 },
-	{ 0xd00c, 0, 8, 0x36 },
-	{ 0xd00d, 0, 2, 0x03 },
-	{ 0xd00a, 0, 8, 0x35 },
-	{ 0xd00b, 0, 2, 0x01 },
-	{ 0x9bc7, 0, 8, 0x07 },
-	{ 0x9bc8, 0, 8, 0x90 },
-	{ 0x9bc3, 0, 8, 0x0f },
-	{ 0x9bc4, 0, 8, 0x02 },
-	{ 0x9bc5, 0, 8, 0x36 },
-	{ 0x9bc6, 0, 8, 0x03 },
-	{ 0x9bba, 0, 8, 0xc9 },
-	{ 0x9bc9, 0, 8, 0x79 },
-	{ 0xd011, 0, 8, 0x10 },
-	{ 0xd012, 0, 2, 0x01 },
-	{ 0xd013, 0, 8, 0x45 },
-	{ 0xd014, 0, 2, 0x03 },
-	{ 0xd040, 0, 8, 0x98 },
-	{ 0xd041, 0, 2, 0x00 },
-	{ 0xd042, 0, 8, 0xcf },
-	{ 0xd043, 0, 2, 0x03 },
-	{ 0xd045, 1, 1, 0x00 },
-	{ 0x9bcf, 0, 1, 0x01 },
-	{ 0xd045, 2, 1, 0x01 },
-	{ 0xd04f, 0, 8, 0x9a },
-	{ 0xd050, 0, 1, 0x01 },
-	{ 0xd051, 0, 8, 0x5a },
-	{ 0xd052, 0, 1, 0x01 },
-	{ 0xd053, 0, 8, 0x50 },
-	{ 0xd054, 0, 8, 0x46 },
-	{ 0x9bd7, 0, 8, 0x0a },
-	{ 0x9bd8, 0, 8, 0x14 },
-	{ 0x9bd9, 0, 8, 0x08 },
-	{ 0x9bd0, 0, 8, 0xcc },
-	{ 0x9be4, 0, 8, 0xa0 },
-	{ 0x9bbd, 0, 8, 0x8e },
-	{ 0x9be2, 0, 8, 0x4d },
-	{ 0x9bee, 0, 1, 0x01 },
+/*
+ * Microtune MT2060 tuner init
+ * AF9013_TUNER_MT2060        0x82
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mt2060[] = {
+	{0x9bd5, 0xff, 0x01},
+	{0x9bd6, 0xff, 0x07},
+	{0xd1a0, 0x02, 0x02},
+	{0xd000, 0x01, 0x01},
+	{0xd000, 0x02, 0x00},
+	{0xd001, 0x02, 0x02},
+	{0xd001, 0x01, 0x00},
+	{0xd001, 0x20, 0x00},
+	{0xd002, 0x1f, 0x19},
+	{0xd003, 0x1f, 0x1a},
+	{0xd004, 0x1f, 0x19},
+	{0xd005, 0x1f, 0x1a},
+	{0xd00e, 0x1f, 0x10},
+	{0xd00f, 0x07, 0x04},
+	{0xd00f, 0x38, 0x28},
+	{0xd010, 0x07, 0x04},
+	{0xd010, 0x38, 0x28},
+	{0xd016, 0xf0, 0x30},
+	{0xd01f, 0x3f, 0x0a},
+	{0xd020, 0x3f, 0x0a},
+	{0x9bda, 0xff, 0x00},
+	{0x9be3, 0xff, 0x00},
+	{0x9bbe, 0x01, 0x00},
+	{0x9bcc, 0x01, 0x00},
+	{0x9bb9, 0xff, 0x75},
+	{0x9bcd, 0xff, 0x24},
+	{0x9bff, 0xff, 0x30},
+	{0xd015, 0xff, 0x46},
+	{0xd016, 0x01, 0x00},
+	{0xd044, 0xff, 0x46},
+	{0xd045, 0x01, 0x00},
+	{0xd008, 0xff, 0x0f},
+	{0xd009, 0x03, 0x02},
+	{0xd006, 0xff, 0x32},
+	{0xd007, 0x03, 0x01},
+	{0xd00c, 0xff, 0x36},
+	{0xd00d, 0x03, 0x03},
+	{0xd00a, 0xff, 0x35},
+	{0xd00b, 0x03, 0x01},
+	{0x9bc7, 0xff, 0x07},
+	{0x9bc8, 0xff, 0x90},
+	{0x9bc3, 0xff, 0x0f},
+	{0x9bc4, 0xff, 0x02},
+	{0x9bc5, 0xff, 0x36},
+	{0x9bc6, 0xff, 0x03},
+	{0x9bba, 0xff, 0xc9},
+	{0x9bc9, 0xff, 0x79},
+	{0xd011, 0xff, 0x10},
+	{0xd012, 0x03, 0x01},
+	{0xd013, 0xff, 0x45},
+	{0xd014, 0x03, 0x03},
+	{0xd040, 0xff, 0x98},
+	{0xd041, 0x03, 0x00},
+	{0xd042, 0xff, 0xcf},
+	{0xd043, 0x03, 0x03},
+	{0xd045, 0x02, 0x00},
+	{0x9bcf, 0x01, 0x01},
+	{0xd045, 0x04, 0x04},
+	{0xd04f, 0xff, 0x9a},
+	{0xd050, 0x01, 0x01},
+	{0xd051, 0xff, 0x5a},
+	{0xd052, 0x01, 0x01},
+	{0xd053, 0xff, 0x50},
+	{0xd054, 0xff, 0x46},
+	{0x9bd7, 0xff, 0x0a},
+	{0x9bd8, 0xff, 0x14},
+	{0x9bd9, 0xff, 0x08},
+	{0x9bd0, 0xff, 0xcc},
+	{0x9be4, 0xff, 0xa0},
+	{0x9bbd, 0xff, 0x8e},
+	{0x9be2, 0xff, 0x4d},
+	{0x9bee, 0x01, 0x01},
 };
 
-/* Microtune MT2060 tuner init
-   AF9013_TUNER_MT2060_2   = 147 */
-static const struct af9013_reg_bit tuner_init_mt2060_2[] = {
-	{ 0x9bd5, 0, 8, 0x01 },
-	{ 0x9bd6, 0, 8, 0x06 },
-	{ 0x9bbe, 0, 8, 0x01 },
-	{ 0xd1a0, 1, 1, 0x01 },
-	{ 0xd000, 0, 1, 0x01 },
-	{ 0xd000, 1, 1, 0x00 },
-	{ 0xd001, 1, 1, 0x01 },
-	{ 0xd001, 0, 1, 0x00 },
-	{ 0xd001, 5, 1, 0x00 },
-	{ 0xd002, 0, 5, 0x19 },
-	{ 0xd003, 0, 5, 0x1a },
-	{ 0xd004, 0, 5, 0x19 },
-	{ 0xd005, 0, 5, 0x1a },
-	{ 0xd00e, 0, 5, 0x10 },
-	{ 0xd00f, 0, 3, 0x04 },
-	{ 0xd00f, 3, 3, 0x05 },
-	{ 0xd010, 0, 3, 0x04 },
-	{ 0xd010, 3, 3, 0x05 },
-	{ 0xd016, 4, 4, 0x03 },
-	{ 0xd01f, 0, 6, 0x0a },
-	{ 0xd020, 0, 6, 0x0a },
-	{ 0xd015, 0, 8, 0x46 },
-	{ 0xd016, 0, 1, 0x00 },
-	{ 0xd044, 0, 8, 0x46 },
-	{ 0xd045, 0, 1, 0x00 },
-	{ 0xd008, 0, 8, 0x0f },
-	{ 0xd009, 0, 2, 0x02 },
-	{ 0xd006, 0, 8, 0x32 },
-	{ 0xd007, 0, 2, 0x01 },
-	{ 0xd00c, 0, 8, 0x36 },
-	{ 0xd00d, 0, 2, 0x03 },
-	{ 0xd00a, 0, 8, 0x35 },
-	{ 0xd00b, 0, 2, 0x01 },
-	{ 0x9bc7, 0, 8, 0x07 },
-	{ 0x9bc8, 0, 8, 0x90 },
-	{ 0x9bc3, 0, 8, 0x0f },
-	{ 0x9bc4, 0, 8, 0x02 },
-	{ 0x9bc5, 0, 8, 0x36 },
-	{ 0x9bc6, 0, 8, 0x03 },
-	{ 0x9bba, 0, 8, 0xc9 },
-	{ 0x9bc9, 0, 8, 0x79 },
-	{ 0xd011, 0, 8, 0x10 },
-	{ 0xd012, 0, 2, 0x01 },
-	{ 0xd013, 0, 8, 0x45 },
-	{ 0xd014, 0, 2, 0x03 },
-	{ 0xd040, 0, 8, 0x98 },
-	{ 0xd041, 0, 2, 0x00 },
-	{ 0xd042, 0, 8, 0xcf },
-	{ 0xd043, 0, 2, 0x03 },
-	{ 0xd045, 1, 1, 0x00 },
-	{ 0x9bcf, 0, 8, 0x01 },
-	{ 0xd045, 2, 1, 0x01 },
-	{ 0xd04f, 0, 8, 0x9a },
-	{ 0xd050, 0, 1, 0x01 },
-	{ 0xd051, 0, 8, 0x5a },
-	{ 0xd052, 0, 1, 0x01 },
-	{ 0xd053, 0, 8, 0x96 },
-	{ 0xd054, 0, 8, 0x46 },
-	{ 0xd045, 7, 1, 0x00 },
-	{ 0x9bd7, 0, 8, 0x0a },
-	{ 0x9bd8, 0, 8, 0x14 },
-	{ 0x9bd9, 0, 8, 0x08 },
+/*
+ * Microtune MT2060 tuner init
+ * AF9013_TUNER_MT2060_2      0x93
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mt2060_2[] = {
+	{0x9bd5, 0xff, 0x01},
+	{0x9bd6, 0xff, 0x06},
+	{0x9bbe, 0xff, 0x01},
+	{0xd1a0, 0x02, 0x02},
+	{0xd000, 0x01, 0x01},
+	{0xd000, 0x02, 0x00},
+	{0xd001, 0x02, 0x02},
+	{0xd001, 0x01, 0x00},
+	{0xd001, 0x20, 0x00},
+	{0xd002, 0x1f, 0x19},
+	{0xd003, 0x1f, 0x1a},
+	{0xd004, 0x1f, 0x19},
+	{0xd005, 0x1f, 0x1a},
+	{0xd00e, 0x1f, 0x10},
+	{0xd00f, 0x07, 0x04},
+	{0xd00f, 0x38, 0x28},
+	{0xd010, 0x07, 0x04},
+	{0xd010, 0x38, 0x28},
+	{0xd016, 0xf0, 0x30},
+	{0xd01f, 0x3f, 0x0a},
+	{0xd020, 0x3f, 0x0a},
+	{0xd015, 0xff, 0x46},
+	{0xd016, 0x01, 0x00},
+	{0xd044, 0xff, 0x46},
+	{0xd045, 0x01, 0x00},
+	{0xd008, 0xff, 0x0f},
+	{0xd009, 0x03, 0x02},
+	{0xd006, 0xff, 0x32},
+	{0xd007, 0x03, 0x01},
+	{0xd00c, 0xff, 0x36},
+	{0xd00d, 0x03, 0x03},
+	{0xd00a, 0xff, 0x35},
+	{0xd00b, 0x03, 0x01},
+	{0x9bc7, 0xff, 0x07},
+	{0x9bc8, 0xff, 0x90},
+	{0x9bc3, 0xff, 0x0f},
+	{0x9bc4, 0xff, 0x02},
+	{0x9bc5, 0xff, 0x36},
+	{0x9bc6, 0xff, 0x03},
+	{0x9bba, 0xff, 0xc9},
+	{0x9bc9, 0xff, 0x79},
+	{0xd011, 0xff, 0x10},
+	{0xd012, 0x03, 0x01},
+	{0xd013, 0xff, 0x45},
+	{0xd014, 0x03, 0x03},
+	{0xd040, 0xff, 0x98},
+	{0xd041, 0x03, 0x00},
+	{0xd042, 0xff, 0xcf},
+	{0xd043, 0x03, 0x03},
+	{0xd045, 0x02, 0x00},
+	{0x9bcf, 0xff, 0x01},
+	{0xd045, 0x04, 0x04},
+	{0xd04f, 0xff, 0x9a},
+	{0xd050, 0x01, 0x01},
+	{0xd051, 0xff, 0x5a},
+	{0xd052, 0x01, 0x01},
+	{0xd053, 0xff, 0x96},
+	{0xd054, 0xff, 0x46},
+	{0xd045, 0x80, 0x00},
+	{0x9bd7, 0xff, 0x0a},
+	{0x9bd8, 0xff, 0x14},
+	{0x9bd9, 0xff, 0x08},
 };
 
-/* MaxLinear MXL5003 tuner init
-   AF9013_TUNER_MXL5003D   =   3 */
-static const struct af9013_reg_bit tuner_init_mxl5003d[] = {
-	{ 0x9bd5, 0, 8, 0x01 },
-	{ 0x9bd6, 0, 8, 0x09 },
-	{ 0xd1a0, 1, 1, 0x01 },
-	{ 0xd000, 0, 1, 0x01 },
-	{ 0xd000, 1, 1, 0x00 },
-	{ 0xd001, 1, 1, 0x01 },
-	{ 0xd001, 0, 1, 0x00 },
-	{ 0xd001, 5, 1, 0x00 },
-	{ 0xd002, 0, 5, 0x19 },
-	{ 0xd003, 0, 5, 0x1a },
-	{ 0xd004, 0, 5, 0x19 },
-	{ 0xd005, 0, 5, 0x1a },
-	{ 0xd00e, 0, 5, 0x10 },
-	{ 0xd00f, 0, 3, 0x04 },
-	{ 0xd00f, 3, 3, 0x05 },
-	{ 0xd010, 0, 3, 0x04 },
-	{ 0xd010, 3, 3, 0x05 },
-	{ 0xd016, 4, 4, 0x03 },
-	{ 0xd01f, 0, 6, 0x0a },
-	{ 0xd020, 0, 6, 0x0a },
-	{ 0x9bda, 0, 8, 0x00 },
-	{ 0x9be3, 0, 8, 0x00 },
-	{ 0x9bfc, 0, 8, 0x0f },
-	{ 0x9bf6, 0, 8, 0x01 },
-	{ 0x9bbe, 0, 1, 0x01 },
-	{ 0xd015, 0, 8, 0x33 },
-	{ 0xd016, 0, 1, 0x00 },
-	{ 0xd044, 0, 8, 0x40 },
-	{ 0xd045, 0, 1, 0x00 },
-	{ 0xd008, 0, 8, 0x0f },
-	{ 0xd009, 0, 2, 0x02 },
-	{ 0xd006, 0, 8, 0x6c },
-	{ 0xd007, 0, 2, 0x00 },
-	{ 0xd00c, 0, 8, 0x3d },
-	{ 0xd00d, 0, 2, 0x00 },
-	{ 0xd00a, 0, 8, 0x45 },
-	{ 0xd00b, 0, 2, 0x01 },
-	{ 0x9bc7, 0, 8, 0x07 },
-	{ 0x9bc8, 0, 8, 0x52 },
-	{ 0x9bc3, 0, 8, 0x0f },
-	{ 0x9bc4, 0, 8, 0x02 },
-	{ 0x9bc5, 0, 8, 0x3d },
-	{ 0x9bc6, 0, 8, 0x00 },
-	{ 0x9bba, 0, 8, 0xa2 },
-	{ 0x9bc9, 0, 8, 0xa0 },
-	{ 0xd011, 0, 8, 0x56 },
-	{ 0xd012, 0, 2, 0x00 },
-	{ 0xd013, 0, 8, 0x50 },
-	{ 0xd014, 0, 2, 0x00 },
-	{ 0xd040, 0, 8, 0x56 },
-	{ 0xd041, 0, 2, 0x00 },
-	{ 0xd042, 0, 8, 0x50 },
-	{ 0xd043, 0, 2, 0x00 },
-	{ 0xd045, 1, 1, 0x00 },
-	{ 0x9bcf, 0, 8, 0x01 },
-	{ 0xd045, 2, 1, 0x01 },
-	{ 0xd04f, 0, 8, 0x9a },
-	{ 0xd050, 0, 1, 0x01 },
-	{ 0xd051, 0, 8, 0x5a },
-	{ 0xd052, 0, 1, 0x01 },
-	{ 0xd053, 0, 8, 0x50 },
-	{ 0xd054, 0, 8, 0x46 },
-	{ 0x9bd7, 0, 8, 0x0a },
-	{ 0x9bd8, 0, 8, 0x14 },
-	{ 0x9bd9, 0, 8, 0x08 },
+/*
+ * MaxLinear MXL5003 tuner init
+ * AF9013_TUNER_MXL5003D      0x03
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mxl5003d[] = {
+	{0x9bd5, 0xff, 0x01},
+	{0x9bd6, 0xff, 0x09},
+	{0xd1a0, 0x02, 0x02},
+	{0xd000, 0x01, 0x01},
+	{0xd000, 0x02, 0x00},
+	{0xd001, 0x02, 0x02},
+	{0xd001, 0x01, 0x00},
+	{0xd001, 0x20, 0x00},
+	{0xd002, 0x1f, 0x19},
+	{0xd003, 0x1f, 0x1a},
+	{0xd004, 0x1f, 0x19},
+	{0xd005, 0x1f, 0x1a},
+	{0xd00e, 0x1f, 0x10},
+	{0xd00f, 0x07, 0x04},
+	{0xd00f, 0x38, 0x28},
+	{0xd010, 0x07, 0x04},
+	{0xd010, 0x38, 0x28},
+	{0xd016, 0xf0, 0x30},
+	{0xd01f, 0x3f, 0x0a},
+	{0xd020, 0x3f, 0x0a},
+	{0x9bda, 0xff, 0x00},
+	{0x9be3, 0xff, 0x00},
+	{0x9bfc, 0xff, 0x0f},
+	{0x9bf6, 0xff, 0x01},
+	{0x9bbe, 0x01, 0x01},
+	{0xd015, 0xff, 0x33},
+	{0xd016, 0x01, 0x00},
+	{0xd044, 0xff, 0x40},
+	{0xd045, 0x01, 0x00},
+	{0xd008, 0xff, 0x0f},
+	{0xd009, 0x03, 0x02},
+	{0xd006, 0xff, 0x6c},
+	{0xd007, 0x03, 0x00},
+	{0xd00c, 0xff, 0x3d},
+	{0xd00d, 0x03, 0x00},
+	{0xd00a, 0xff, 0x45},
+	{0xd00b, 0x03, 0x01},
+	{0x9bc7, 0xff, 0x07},
+	{0x9bc8, 0xff, 0x52},
+	{0x9bc3, 0xff, 0x0f},
+	{0x9bc4, 0xff, 0x02},
+	{0x9bc5, 0xff, 0x3d},
+	{0x9bc6, 0xff, 0x00},
+	{0x9bba, 0xff, 0xa2},
+	{0x9bc9, 0xff, 0xa0},
+	{0xd011, 0xff, 0x56},
+	{0xd012, 0x03, 0x00},
+	{0xd013, 0xff, 0x50},
+	{0xd014, 0x03, 0x00},
+	{0xd040, 0xff, 0x56},
+	{0xd041, 0x03, 0x00},
+	{0xd042, 0xff, 0x50},
+	{0xd043, 0x03, 0x00},
+	{0xd045, 0x02, 0x00},
+	{0x9bcf, 0xff, 0x01},
+	{0xd045, 0x04, 0x04},
+	{0xd04f, 0xff, 0x9a},
+	{0xd050, 0x01, 0x01},
+	{0xd051, 0xff, 0x5a},
+	{0xd052, 0x01, 0x01},
+	{0xd053, 0xff, 0x50},
+	{0xd054, 0xff, 0x46},
+	{0x9bd7, 0xff, 0x0a},
+	{0x9bd8, 0xff, 0x14},
+	{0x9bd9, 0xff, 0x08},
 };
 
-/* MaxLinear MXL5005S & MXL5007T tuner init
-   AF9013_TUNER_MXL5005D   =  13
-   AF9013_TUNER_MXL5005R   =  30
-   AF9013_TUNER_MXL5007T   = 177 */
-static const struct af9013_reg_bit tuner_init_mxl5005[] = {
-	{ 0x9bd5, 0, 8, 0x01 },
-	{ 0x9bd6, 0, 8, 0x07 },
-	{ 0xd1a0, 1, 1, 0x01 },
-	{ 0xd000, 0, 1, 0x01 },
-	{ 0xd000, 1, 1, 0x00 },
-	{ 0xd001, 1, 1, 0x01 },
-	{ 0xd001, 0, 1, 0x00 },
-	{ 0xd001, 5, 1, 0x00 },
-	{ 0xd002, 0, 5, 0x19 },
-	{ 0xd003, 0, 5, 0x1a },
-	{ 0xd004, 0, 5, 0x19 },
-	{ 0xd005, 0, 5, 0x1a },
-	{ 0xd00e, 0, 5, 0x10 },
-	{ 0xd00f, 0, 3, 0x04 },
-	{ 0xd00f, 3, 3, 0x05 },
-	{ 0xd010, 0, 3, 0x04 },
-	{ 0xd010, 3, 3, 0x05 },
-	{ 0xd016, 4, 4, 0x03 },
-	{ 0xd01f, 0, 6, 0x0a },
-	{ 0xd020, 0, 6, 0x0a },
-	{ 0x9bda, 0, 8, 0x01 },
-	{ 0x9be3, 0, 8, 0x01 },
-	{ 0x9bbe, 0, 1, 0x01 },
-	{ 0x9bcc, 0, 1, 0x01 },
-	{ 0x9bb9, 0, 8, 0x00 },
-	{ 0x9bcd, 0, 8, 0x28 },
-	{ 0x9bff, 0, 8, 0x24 },
-	{ 0xd015, 0, 8, 0x40 },
-	{ 0xd016, 0, 1, 0x00 },
-	{ 0xd044, 0, 8, 0x40 },
-	{ 0xd045, 0, 1, 0x00 },
-	{ 0xd008, 0, 8, 0x0f },
-	{ 0xd009, 0, 2, 0x02 },
-	{ 0xd006, 0, 8, 0x73 },
-	{ 0xd007, 0, 2, 0x01 },
-	{ 0xd00c, 0, 8, 0xfa },
-	{ 0xd00d, 0, 2, 0x01 },
-	{ 0xd00a, 0, 8, 0xff },
-	{ 0xd00b, 0, 2, 0x01 },
-	{ 0x9bc7, 0, 8, 0x23 },
-	{ 0x9bc8, 0, 8, 0x55 },
-	{ 0x9bc3, 0, 8, 0x01 },
-	{ 0x9bc4, 0, 8, 0x02 },
-	{ 0x9bc5, 0, 8, 0xfa },
-	{ 0x9bc6, 0, 8, 0x01 },
-	{ 0x9bba, 0, 8, 0xff },
-	{ 0x9bc9, 0, 8, 0xff },
-	{ 0x9bd3, 0, 8, 0x95 },
-	{ 0xd011, 0, 8, 0x70 },
-	{ 0xd012, 0, 2, 0x01 },
-	{ 0xd013, 0, 8, 0xfb },
-	{ 0xd014, 0, 2, 0x01 },
-	{ 0xd040, 0, 8, 0x70 },
-	{ 0xd041, 0, 2, 0x01 },
-	{ 0xd042, 0, 8, 0xfb },
-	{ 0xd043, 0, 2, 0x01 },
-	{ 0xd045, 1, 1, 0x00 },
-	{ 0x9bcf, 0, 1, 0x01 },
-	{ 0xd045, 2, 1, 0x01 },
-	{ 0xd04f, 0, 8, 0x9a },
-	{ 0xd050, 0, 1, 0x01 },
-	{ 0xd051, 0, 8, 0x5a },
-	{ 0xd052, 0, 1, 0x01 },
-	{ 0xd053, 0, 8, 0x50 },
-	{ 0xd054, 0, 8, 0x46 },
-	{ 0x9bd7, 0, 8, 0x0a },
-	{ 0x9bd8, 0, 8, 0x14 },
-	{ 0x9bd9, 0, 8, 0x08 },
-	{ 0x9bd0, 0, 8, 0x93 },
-	{ 0x9be4, 0, 8, 0xfe },
-	{ 0x9bbd, 0, 8, 0x63 },
-	{ 0x9be2, 0, 8, 0xfe },
-	{ 0x9bee, 0, 1, 0x01 },
+/*
+ * MaxLinear MXL5005S & MXL5007T tuner init
+ * AF9013_TUNER_MXL5005D      0x0d
+ * AF9013_TUNER_MXL5005R      0x1e
+ * AF9013_TUNER_MXL5007T      0xb1
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mxl5005[] = {
+	{0x9bd5, 0xff, 0x01},
+	{0x9bd6, 0xff, 0x07},
+	{0xd1a0, 0x02, 0x02},
+	{0xd000, 0x01, 0x01},
+	{0xd000, 0x02, 0x00},
+	{0xd001, 0x02, 0x02},
+	{0xd001, 0x01, 0x00},
+	{0xd001, 0x20, 0x00},
+	{0xd002, 0x1f, 0x19},
+	{0xd003, 0x1f, 0x1a},
+	{0xd004, 0x1f, 0x19},
+	{0xd005, 0x1f, 0x1a},
+	{0xd00e, 0x1f, 0x10},
+	{0xd00f, 0x07, 0x04},
+	{0xd00f, 0x38, 0x28},
+	{0xd010, 0x07, 0x04},
+	{0xd010, 0x38, 0x28},
+	{0xd016, 0xf0, 0x30},
+	{0xd01f, 0x3f, 0x0a},
+	{0xd020, 0x3f, 0x0a},
+	{0x9bda, 0xff, 0x01},
+	{0x9be3, 0xff, 0x01},
+	{0x9bbe, 0x01, 0x01},
+	{0x9bcc, 0x01, 0x01},
+	{0x9bb9, 0xff, 0x00},
+	{0x9bcd, 0xff, 0x28},
+	{0x9bff, 0xff, 0x24},
+	{0xd015, 0xff, 0x40},
+	{0xd016, 0x01, 0x00},
+	{0xd044, 0xff, 0x40},
+	{0xd045, 0x01, 0x00},
+	{0xd008, 0xff, 0x0f},
+	{0xd009, 0x03, 0x02},
+	{0xd006, 0xff, 0x73},
+	{0xd007, 0x03, 0x01},
+	{0xd00c, 0xff, 0xfa},
+	{0xd00d, 0x03, 0x01},
+	{0xd00a, 0xff, 0xff},
+	{0xd00b, 0x03, 0x01},
+	{0x9bc7, 0xff, 0x23},
+	{0x9bc8, 0xff, 0x55},
+	{0x9bc3, 0xff, 0x01},
+	{0x9bc4, 0xff, 0x02},
+	{0x9bc5, 0xff, 0xfa},
+	{0x9bc6, 0xff, 0x01},
+	{0x9bba, 0xff, 0xff},
+	{0x9bc9, 0xff, 0xff},
+	{0x9bd3, 0xff, 0x95},
+	{0xd011, 0xff, 0x70},
+	{0xd012, 0x03, 0x01},
+	{0xd013, 0xff, 0xfb},
+	{0xd014, 0x03, 0x01},
+	{0xd040, 0xff, 0x70},
+	{0xd041, 0x03, 0x01},
+	{0xd042, 0xff, 0xfb},
+	{0xd043, 0x03, 0x01},
+	{0xd045, 0x02, 0x00},
+	{0x9bcf, 0x01, 0x01},
+	{0xd045, 0x04, 0x04},
+	{0xd04f, 0xff, 0x9a},
+	{0xd050, 0x01, 0x01},
+	{0xd051, 0xff, 0x5a},
+	{0xd052, 0x01, 0x01},
+	{0xd053, 0xff, 0x50},
+	{0xd054, 0xff, 0x46},
+	{0x9bd7, 0xff, 0x0a},
+	{0x9bd8, 0xff, 0x14},
+	{0x9bd9, 0xff, 0x08},
+	{0x9bd0, 0xff, 0x93},
+	{0x9be4, 0xff, 0xfe},
+	{0x9bbd, 0xff, 0x63},
+	{0x9be2, 0xff, 0xfe},
+	{0x9bee, 0x01, 0x01},
 };
 
-/* Quantek QT1010 tuner init
-   AF9013_TUNER_QT1010     = 134
-   AF9013_TUNER_QT1010A    = 162 */
-static const struct af9013_reg_bit tuner_init_qt1010[] = {
-	{ 0x9bd5, 0, 8, 0x01 },
-	{ 0x9bd6, 0, 8, 0x09 },
-	{ 0xd1a0, 1, 1, 0x01 },
-	{ 0xd000, 0, 1, 0x01 },
-	{ 0xd000, 1, 1, 0x00 },
-	{ 0xd001, 1, 1, 0x01 },
-	{ 0xd001, 0, 1, 0x00 },
-	{ 0xd001, 5, 1, 0x00 },
-	{ 0xd002, 0, 5, 0x19 },
-	{ 0xd003, 0, 5, 0x1a },
-	{ 0xd004, 0, 5, 0x19 },
-	{ 0xd005, 0, 5, 0x1a },
-	{ 0xd00e, 0, 5, 0x10 },
-	{ 0xd00f, 0, 3, 0x04 },
-	{ 0xd00f, 3, 3, 0x05 },
-	{ 0xd010, 0, 3, 0x04 },
-	{ 0xd010, 3, 3, 0x05 },
-	{ 0xd016, 4, 4, 0x03 },
-	{ 0xd01f, 0, 6, 0x0a },
-	{ 0xd020, 0, 6, 0x0a },
-	{ 0x9bda, 0, 8, 0x01 },
-	{ 0x9be3, 0, 8, 0x01 },
-	{ 0xd015, 0, 8, 0x46 },
-	{ 0xd016, 0, 1, 0x00 },
-	{ 0xd044, 0, 8, 0x46 },
-	{ 0xd045, 0, 1, 0x00 },
-	{ 0x9bbe, 0, 1, 0x01 },
-	{ 0x9bcc, 0, 1, 0x01 },
-	{ 0x9bb9, 0, 8, 0x00 },
-	{ 0x9bcd, 0, 8, 0x28 },
-	{ 0x9bff, 0, 8, 0x20 },
-	{ 0xd008, 0, 8, 0x0f },
-	{ 0xd009, 0, 2, 0x02 },
-	{ 0xd006, 0, 8, 0x99 },
-	{ 0xd007, 0, 2, 0x01 },
-	{ 0xd00c, 0, 8, 0x0f },
-	{ 0xd00d, 0, 2, 0x02 },
-	{ 0xd00a, 0, 8, 0x50 },
-	{ 0xd00b, 0, 2, 0x01 },
-	{ 0x9bc7, 0, 8, 0x00 },
-	{ 0x9bc8, 0, 8, 0x00 },
-	{ 0x9bc3, 0, 8, 0x0f },
-	{ 0x9bc4, 0, 8, 0x02 },
-	{ 0x9bc5, 0, 8, 0x0f },
-	{ 0x9bc6, 0, 8, 0x02 },
-	{ 0x9bba, 0, 8, 0xc5 },
-	{ 0x9bc9, 0, 8, 0xff },
-	{ 0xd011, 0, 8, 0x58 },
-	{ 0xd012, 0, 2, 0x02 },
-	{ 0xd013, 0, 8, 0x89 },
-	{ 0xd014, 0, 2, 0x01 },
-	{ 0xd040, 0, 8, 0x58 },
-	{ 0xd041, 0, 2, 0x02 },
-	{ 0xd042, 0, 8, 0x89 },
-	{ 0xd043, 0, 2, 0x01 },
-	{ 0xd045, 1, 1, 0x00 },
-	{ 0x9bcf, 0, 1, 0x01 },
-	{ 0xd045, 2, 1, 0x01 },
-	{ 0xd04f, 0, 8, 0x9a },
-	{ 0xd050, 0, 1, 0x01 },
-	{ 0xd051, 0, 8, 0x5a },
-	{ 0xd052, 0, 1, 0x01 },
-	{ 0xd053, 0, 8, 0x50 },
-	{ 0xd054, 0, 8, 0x46 },
-	{ 0x9bd7, 0, 8, 0x0a },
-	{ 0x9bd8, 0, 8, 0x14 },
-	{ 0x9bd9, 0, 8, 0x08 },
-	{ 0x9bd0, 0, 8, 0xcd },
-	{ 0x9be4, 0, 8, 0xbb },
-	{ 0x9bbd, 0, 8, 0x93 },
-	{ 0x9be2, 0, 8, 0x80 },
-	{ 0x9bee, 0, 1, 0x01 },
+/*
+ * Quantek QT1010 tuner init
+ * AF9013_TUNER_QT1010        0x86
+ * AF9013_TUNER_QT1010A       0xa2
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_qt1010[] = {
+	{0x9bd5, 0xff, 0x01},
+	{0x9bd6, 0xff, 0x09},
+	{0xd1a0, 0x02, 0x02},
+	{0xd000, 0x01, 0x01},
+	{0xd000, 0x02, 0x00},
+	{0xd001, 0x02, 0x02},
+	{0xd001, 0x01, 0x00},
+	{0xd001, 0x20, 0x00},
+	{0xd002, 0x1f, 0x19},
+	{0xd003, 0x1f, 0x1a},
+	{0xd004, 0x1f, 0x19},
+	{0xd005, 0x1f, 0x1a},
+	{0xd00e, 0x1f, 0x10},
+	{0xd00f, 0x07, 0x04},
+	{0xd00f, 0x38, 0x28},
+	{0xd010, 0x07, 0x04},
+	{0xd010, 0x38, 0x28},
+	{0xd016, 0xf0, 0x30},
+	{0xd01f, 0x3f, 0x0a},
+	{0xd020, 0x3f, 0x0a},
+	{0x9bda, 0xff, 0x01},
+	{0x9be3, 0xff, 0x01},
+	{0xd015, 0xff, 0x46},
+	{0xd016, 0x01, 0x00},
+	{0xd044, 0xff, 0x46},
+	{0xd045, 0x01, 0x00},
+	{0x9bbe, 0x01, 0x01},
+	{0x9bcc, 0x01, 0x01},
+	{0x9bb9, 0xff, 0x00},
+	{0x9bcd, 0xff, 0x28},
+	{0x9bff, 0xff, 0x20},
+	{0xd008, 0xff, 0x0f},
+	{0xd009, 0x03, 0x02},
+	{0xd006, 0xff, 0x99},
+	{0xd007, 0x03, 0x01},
+	{0xd00c, 0xff, 0x0f},
+	{0xd00d, 0x03, 0x02},
+	{0xd00a, 0xff, 0x50},
+	{0xd00b, 0x03, 0x01},
+	{0x9bc7, 0xff, 0x00},
+	{0x9bc8, 0xff, 0x00},
+	{0x9bc3, 0xff, 0x0f},
+	{0x9bc4, 0xff, 0x02},
+	{0x9bc5, 0xff, 0x0f},
+	{0x9bc6, 0xff, 0x02},
+	{0x9bba, 0xff, 0xc5},
+	{0x9bc9, 0xff, 0xff},
+	{0xd011, 0xff, 0x58},
+	{0xd012, 0x03, 0x02},
+	{0xd013, 0xff, 0x89},
+	{0xd014, 0x03, 0x01},
+	{0xd040, 0xff, 0x58},
+	{0xd041, 0x03, 0x02},
+	{0xd042, 0xff, 0x89},
+	{0xd043, 0x03, 0x01},
+	{0xd045, 0x02, 0x00},
+	{0x9bcf, 0x01, 0x01},
+	{0xd045, 0x04, 0x04},
+	{0xd04f, 0xff, 0x9a},
+	{0xd050, 0x01, 0x01},
+	{0xd051, 0xff, 0x5a},
+	{0xd052, 0x01, 0x01},
+	{0xd053, 0xff, 0x50},
+	{0xd054, 0xff, 0x46},
+	{0x9bd7, 0xff, 0x0a},
+	{0x9bd8, 0xff, 0x14},
+	{0x9bd9, 0xff, 0x08},
+	{0x9bd0, 0xff, 0xcd},
+	{0x9be4, 0xff, 0xbb},
+	{0x9bbd, 0xff, 0x93},
+	{0x9be2, 0xff, 0x80},
+	{0x9bee, 0x01, 0x01},
 };
 
-/* Freescale MC44S803 tuner init
-   AF9013_TUNER_MC44S803   = 133 */
-static const struct af9013_reg_bit tuner_init_mc44s803[] = {
-	{ 0x9bd5, 0, 8, 0x01 },
-	{ 0x9bd6, 0, 8, 0x06 },
-	{ 0xd1a0, 1, 1, 0x01 },
-	{ 0xd000, 0, 1, 0x01 },
-	{ 0xd000, 1, 1, 0x00 },
-	{ 0xd001, 1, 1, 0x01 },
-	{ 0xd001, 0, 1, 0x00 },
-	{ 0xd001, 5, 1, 0x00 },
-	{ 0xd002, 0, 5, 0x19 },
-	{ 0xd003, 0, 5, 0x1a },
-	{ 0xd004, 0, 5, 0x19 },
-	{ 0xd005, 0, 5, 0x1a },
-	{ 0xd00e, 0, 5, 0x10 },
-	{ 0xd00f, 0, 3, 0x04 },
-	{ 0xd00f, 3, 3, 0x05 },
-	{ 0xd010, 0, 3, 0x04 },
-	{ 0xd010, 3, 3, 0x05 },
-	{ 0xd016, 4, 4, 0x03 },
-	{ 0xd01f, 0, 6, 0x0a },
-	{ 0xd020, 0, 6, 0x0a },
-	{ 0x9bda, 0, 8, 0x00 },
-	{ 0x9be3, 0, 8, 0x00 },
-	{ 0x9bf6, 0, 8, 0x01 },
-	{ 0x9bf8, 0, 8, 0x02 },
-	{ 0x9bf9, 0, 8, 0x02 },
-	{ 0x9bfc, 0, 8, 0x1f },
-	{ 0x9bbe, 0, 1, 0x01 },
-	{ 0x9bcc, 0, 1, 0x01 },
-	{ 0x9bb9, 0, 8, 0x00 },
-	{ 0x9bcd, 0, 8, 0x24 },
-	{ 0x9bff, 0, 8, 0x24 },
-	{ 0xd015, 0, 8, 0x46 },
-	{ 0xd016, 0, 1, 0x00 },
-	{ 0xd044, 0, 8, 0x46 },
-	{ 0xd045, 0, 1, 0x00 },
-	{ 0xd008, 0, 8, 0x01 },
-	{ 0xd009, 0, 2, 0x02 },
-	{ 0xd006, 0, 8, 0x7b },
-	{ 0xd007, 0, 2, 0x00 },
-	{ 0xd00c, 0, 8, 0x7c },
-	{ 0xd00d, 0, 2, 0x02 },
-	{ 0xd00a, 0, 8, 0xfe },
-	{ 0xd00b, 0, 2, 0x01 },
-	{ 0x9bc7, 0, 8, 0x08 },
-	{ 0x9bc8, 0, 8, 0x9a },
-	{ 0x9bc3, 0, 8, 0x01 },
-	{ 0x9bc4, 0, 8, 0x02 },
-	{ 0x9bc5, 0, 8, 0x7c },
-	{ 0x9bc6, 0, 8, 0x02 },
-	{ 0x9bba, 0, 8, 0xfc },
-	{ 0x9bc9, 0, 8, 0xaa },
-	{ 0xd011, 0, 8, 0x6b },
-	{ 0xd012, 0, 2, 0x00 },
-	{ 0xd013, 0, 8, 0x88 },
-	{ 0xd014, 0, 2, 0x02 },
-	{ 0xd040, 0, 8, 0x6b },
-	{ 0xd041, 0, 2, 0x00 },
-	{ 0xd042, 0, 8, 0x7c },
-	{ 0xd043, 0, 2, 0x02 },
-	{ 0xd045, 1, 1, 0x00 },
-	{ 0x9bcf, 0, 1, 0x01 },
-	{ 0xd045, 2, 1, 0x01 },
-	{ 0xd04f, 0, 8, 0x9a },
-	{ 0xd050, 0, 1, 0x01 },
-	{ 0xd051, 0, 8, 0x5a },
-	{ 0xd052, 0, 1, 0x01 },
-	{ 0xd053, 0, 8, 0x50 },
-	{ 0xd054, 0, 8, 0x46 },
-	{ 0x9bd7, 0, 8, 0x0a },
-	{ 0x9bd8, 0, 8, 0x14 },
-	{ 0x9bd9, 0, 8, 0x08 },
-	{ 0x9bd0, 0, 8, 0x9e },
-	{ 0x9be4, 0, 8, 0xff },
-	{ 0x9bbd, 0, 8, 0x9e },
-	{ 0x9be2, 0, 8, 0x25 },
-	{ 0x9bee, 0, 1, 0x01 },
-	{ 0xd73b, 3, 1, 0x00 },
+/*
+ * Freescale MC44S803 tuner init
+ * AF9013_TUNER_MC44S803      0x85
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mc44s803[] = {
+	{0x9bd5, 0xff, 0x01},
+	{0x9bd6, 0xff, 0x06},
+	{0xd1a0, 0x02, 0x02},
+	{0xd000, 0x01, 0x01},
+	{0xd000, 0x02, 0x00},
+	{0xd001, 0x02, 0x02},
+	{0xd001, 0x01, 0x00},
+	{0xd001, 0x20, 0x00},
+	{0xd002, 0x1f, 0x19},
+	{0xd003, 0x1f, 0x1a},
+	{0xd004, 0x1f, 0x19},
+	{0xd005, 0x1f, 0x1a},
+	{0xd00e, 0x1f, 0x10},
+	{0xd00f, 0x07, 0x04},
+	{0xd00f, 0x38, 0x28},
+	{0xd010, 0x07, 0x04},
+	{0xd010, 0x38, 0x28},
+	{0xd016, 0xf0, 0x30},
+	{0xd01f, 0x3f, 0x0a},
+	{0xd020, 0x3f, 0x0a},
+	{0x9bda, 0xff, 0x00},
+	{0x9be3, 0xff, 0x00},
+	{0x9bf6, 0xff, 0x01},
+	{0x9bf8, 0xff, 0x02},
+	{0x9bf9, 0xff, 0x02},
+	{0x9bfc, 0xff, 0x1f},
+	{0x9bbe, 0x01, 0x01},
+	{0x9bcc, 0x01, 0x01},
+	{0x9bb9, 0xff, 0x00},
+	{0x9bcd, 0xff, 0x24},
+	{0x9bff, 0xff, 0x24},
+	{0xd015, 0xff, 0x46},
+	{0xd016, 0x01, 0x00},
+	{0xd044, 0xff, 0x46},
+	{0xd045, 0x01, 0x00},
+	{0xd008, 0xff, 0x01},
+	{0xd009, 0x03, 0x02},
+	{0xd006, 0xff, 0x7b},
+	{0xd007, 0x03, 0x00},
+	{0xd00c, 0xff, 0x7c},
+	{0xd00d, 0x03, 0x02},
+	{0xd00a, 0xff, 0xfe},
+	{0xd00b, 0x03, 0x01},
+	{0x9bc7, 0xff, 0x08},
+	{0x9bc8, 0xff, 0x9a},
+	{0x9bc3, 0xff, 0x01},
+	{0x9bc4, 0xff, 0x02},
+	{0x9bc5, 0xff, 0x7c},
+	{0x9bc6, 0xff, 0x02},
+	{0x9bba, 0xff, 0xfc},
+	{0x9bc9, 0xff, 0xaa},
+	{0xd011, 0xff, 0x6b},
+	{0xd012, 0x03, 0x00},
+	{0xd013, 0xff, 0x88},
+	{0xd014, 0x03, 0x02},
+	{0xd040, 0xff, 0x6b},
+	{0xd041, 0x03, 0x00},
+	{0xd042, 0xff, 0x7c},
+	{0xd043, 0x03, 0x02},
+	{0xd045, 0x02, 0x00},
+	{0x9bcf, 0x01, 0x01},
+	{0xd045, 0x04, 0x04},
+	{0xd04f, 0xff, 0x9a},
+	{0xd050, 0x01, 0x01},
+	{0xd051, 0xff, 0x5a},
+	{0xd052, 0x01, 0x01},
+	{0xd053, 0xff, 0x50},
+	{0xd054, 0xff, 0x46},
+	{0x9bd7, 0xff, 0x0a},
+	{0x9bd8, 0xff, 0x14},
+	{0x9bd9, 0xff, 0x08},
+	{0x9bd0, 0xff, 0x9e},
+	{0x9be4, 0xff, 0xff},
+	{0x9bbd, 0xff, 0x9e},
+	{0x9be2, 0xff, 0x25},
+	{0x9bee, 0x01, 0x01},
+	{0xd73b, 0x08, 0x00},
 };
 
-/* unknown, probably for tin can tuner, tuner init
-   AF9013_TUNER_UNKNOWN   = 140 */
-static const struct af9013_reg_bit tuner_init_unknown[] = {
-	{ 0x9bd5, 0, 8, 0x01 },
-	{ 0x9bd6, 0, 8, 0x02 },
-	{ 0xd1a0, 1, 1, 0x01 },
-	{ 0xd000, 0, 1, 0x01 },
-	{ 0xd000, 1, 1, 0x00 },
-	{ 0xd001, 1, 1, 0x01 },
-	{ 0xd001, 0, 1, 0x00 },
-	{ 0xd001, 5, 1, 0x00 },
-	{ 0xd002, 0, 5, 0x19 },
-	{ 0xd003, 0, 5, 0x1a },
-	{ 0xd004, 0, 5, 0x19 },
-	{ 0xd005, 0, 5, 0x1a },
-	{ 0xd00e, 0, 5, 0x10 },
-	{ 0xd00f, 0, 3, 0x04 },
-	{ 0xd00f, 3, 3, 0x05 },
-	{ 0xd010, 0, 3, 0x04 },
-	{ 0xd010, 3, 3, 0x05 },
-	{ 0xd016, 4, 4, 0x03 },
-	{ 0xd01f, 0, 6, 0x0a },
-	{ 0xd020, 0, 6, 0x0a },
-	{ 0x9bda, 0, 8, 0x01 },
-	{ 0x9be3, 0, 8, 0x01 },
-	{ 0xd1a0, 1, 1, 0x00 },
-	{ 0x9bbe, 0, 1, 0x01 },
-	{ 0x9bcc, 0, 1, 0x01 },
-	{ 0x9bb9, 0, 8, 0x00 },
-	{ 0x9bcd, 0, 8, 0x18 },
-	{ 0x9bff, 0, 8, 0x2c },
-	{ 0xd015, 0, 8, 0x46 },
-	{ 0xd016, 0, 1, 0x00 },
-	{ 0xd044, 0, 8, 0x46 },
-	{ 0xd045, 0, 1, 0x00 },
-	{ 0xd008, 0, 8, 0xdf },
-	{ 0xd009, 0, 2, 0x02 },
-	{ 0xd006, 0, 8, 0x44 },
-	{ 0xd007, 0, 2, 0x01 },
-	{ 0xd00c, 0, 8, 0x00 },
-	{ 0xd00d, 0, 2, 0x02 },
-	{ 0xd00a, 0, 8, 0xf6 },
-	{ 0xd00b, 0, 2, 0x01 },
-	{ 0x9bba, 0, 8, 0xf9 },
-	{ 0x9bc8, 0, 8, 0xaa },
-	{ 0x9bc3, 0, 8, 0xdf },
-	{ 0x9bc4, 0, 8, 0x02 },
-	{ 0x9bc5, 0, 8, 0x00 },
-	{ 0x9bc6, 0, 8, 0x02 },
-	{ 0x9bc9, 0, 8, 0xf0 },
-	{ 0xd011, 0, 8, 0x3c },
-	{ 0xd012, 0, 2, 0x01 },
-	{ 0xd013, 0, 8, 0xf7 },
-	{ 0xd014, 0, 2, 0x02 },
-	{ 0xd040, 0, 8, 0x0b },
-	{ 0xd041, 0, 2, 0x02 },
-	{ 0xd042, 0, 8, 0x4d },
-	{ 0xd043, 0, 2, 0x00 },
-	{ 0xd045, 1, 1, 0x00 },
-	{ 0x9bcf, 0, 1, 0x01 },
-	{ 0xd045, 2, 1, 0x01 },
-	{ 0xd04f, 0, 8, 0x9a },
-	{ 0xd050, 0, 1, 0x01 },
-	{ 0xd051, 0, 8, 0x5a },
-	{ 0xd052, 0, 1, 0x01 },
-	{ 0xd053, 0, 8, 0x50 },
-	{ 0xd054, 0, 8, 0x46 },
-	{ 0x9bd7, 0, 8, 0x0a },
-	{ 0x9bd8, 0, 8, 0x14 },
-	{ 0x9bd9, 0, 8, 0x08 },
+/*
+ * Unknown, probably for tin can tuner, tuner init
+ * AF9013_TUNER_UNKNOWN       0x8c
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_unknown[] = {
+	{0x9bd5, 0xff, 0x01},
+	{0x9bd6, 0xff, 0x02},
+	{0xd1a0, 0x02, 0x02},
+	{0xd000, 0x01, 0x01},
+	{0xd000, 0x02, 0x00},
+	{0xd001, 0x02, 0x02},
+	{0xd001, 0x01, 0x00},
+	{0xd001, 0x20, 0x00},
+	{0xd002, 0x1f, 0x19},
+	{0xd003, 0x1f, 0x1a},
+	{0xd004, 0x1f, 0x19},
+	{0xd005, 0x1f, 0x1a},
+	{0xd00e, 0x1f, 0x10},
+	{0xd00f, 0x07, 0x04},
+	{0xd00f, 0x38, 0x28},
+	{0xd010, 0x07, 0x04},
+	{0xd010, 0x38, 0x28},
+	{0xd016, 0xf0, 0x30},
+	{0xd01f, 0x3f, 0x0a},
+	{0xd020, 0x3f, 0x0a},
+	{0x9bda, 0xff, 0x01},
+	{0x9be3, 0xff, 0x01},
+	{0xd1a0, 0x02, 0x00},
+	{0x9bbe, 0x01, 0x01},
+	{0x9bcc, 0x01, 0x01},
+	{0x9bb9, 0xff, 0x00},
+	{0x9bcd, 0xff, 0x18},
+	{0x9bff, 0xff, 0x2c},
+	{0xd015, 0xff, 0x46},
+	{0xd016, 0x01, 0x00},
+	{0xd044, 0xff, 0x46},
+	{0xd045, 0x01, 0x00},
+	{0xd008, 0xff, 0xdf},
+	{0xd009, 0x03, 0x02},
+	{0xd006, 0xff, 0x44},
+	{0xd007, 0x03, 0x01},
+	{0xd00c, 0xff, 0x00},
+	{0xd00d, 0x03, 0x02},
+	{0xd00a, 0xff, 0xf6},
+	{0xd00b, 0x03, 0x01},
+	{0x9bba, 0xff, 0xf9},
+	{0x9bc8, 0xff, 0xaa},
+	{0x9bc3, 0xff, 0xdf},
+	{0x9bc4, 0xff, 0x02},
+	{0x9bc5, 0xff, 0x00},
+	{0x9bc6, 0xff, 0x02},
+	{0x9bc9, 0xff, 0xf0},
+	{0xd011, 0xff, 0x3c},
+	{0xd012, 0x03, 0x01},
+	{0xd013, 0xff, 0xf7},
+	{0xd014, 0x03, 0x02},
+	{0xd040, 0xff, 0x0b},
+	{0xd041, 0x03, 0x02},
+	{0xd042, 0xff, 0x4d},
+	{0xd043, 0x03, 0x00},
+	{0xd045, 0x02, 0x00},
+	{0x9bcf, 0x01, 0x01},
+	{0xd045, 0x04, 0x04},
+	{0xd04f, 0xff, 0x9a},
+	{0xd050, 0x01, 0x01},
+	{0xd051, 0xff, 0x5a},
+	{0xd052, 0x01, 0x01},
+	{0xd053, 0xff, 0x50},
+	{0xd054, 0xff, 0x46},
+	{0x9bd7, 0xff, 0x0a},
+	{0x9bd8, 0xff, 0x14},
+	{0x9bd9, 0xff, 0x08},
 };
 
-/* NXP TDA18271 & TDA18218 tuner init
-   AF9013_TUNER_TDA18271   = 156
-   AF9013_TUNER_TDA18218   = 179 */
-static const struct af9013_reg_bit tuner_init_tda18271[] = {
-	{ 0x9bd5, 0, 8, 0x01 },
-	{ 0x9bd6, 0, 8, 0x04 },
-	{ 0xd1a0, 1, 1, 0x01 },
-	{ 0xd000, 0, 1, 0x01 },
-	{ 0xd000, 1, 1, 0x00 },
-	{ 0xd001, 1, 1, 0x01 },
-	{ 0xd001, 0, 1, 0x00 },
-	{ 0xd001, 5, 1, 0x00 },
-	{ 0xd002, 0, 5, 0x19 },
-	{ 0xd003, 0, 5, 0x1a },
-	{ 0xd004, 0, 5, 0x19 },
-	{ 0xd005, 0, 5, 0x1a },
-	{ 0xd00e, 0, 5, 0x10 },
-	{ 0xd00f, 0, 3, 0x04 },
-	{ 0xd00f, 3, 3, 0x05 },
-	{ 0xd010, 0, 3, 0x04 },
-	{ 0xd010, 3, 3, 0x05 },
-	{ 0xd016, 4, 4, 0x03 },
-	{ 0xd01f, 0, 6, 0x0a },
-	{ 0xd020, 0, 6, 0x0a },
-	{ 0x9bda, 0, 8, 0x01 },
-	{ 0x9be3, 0, 8, 0x01 },
-	{ 0xd1a0, 1, 1, 0x00 },
-	{ 0x9bbe, 0, 1, 0x01 },
-	{ 0x9bcc, 0, 1, 0x01 },
-	{ 0x9bb9, 0, 8, 0x00 },
-	{ 0x9bcd, 0, 8, 0x18 },
-	{ 0x9bff, 0, 8, 0x2c },
-	{ 0xd015, 0, 8, 0x46 },
-	{ 0xd016, 0, 1, 0x00 },
-	{ 0xd044, 0, 8, 0x46 },
-	{ 0xd045, 0, 1, 0x00 },
-	{ 0xd008, 0, 8, 0xdf },
-	{ 0xd009, 0, 2, 0x02 },
-	{ 0xd006, 0, 8, 0x44 },
-	{ 0xd007, 0, 2, 0x01 },
-	{ 0xd00c, 0, 8, 0x00 },
-	{ 0xd00d, 0, 2, 0x02 },
-	{ 0xd00a, 0, 8, 0xf6 },
-	{ 0xd00b, 0, 2, 0x01 },
-	{ 0x9bba, 0, 8, 0xf9 },
-	{ 0x9bc8, 0, 8, 0xaa },
-	{ 0x9bc3, 0, 8, 0xdf },
-	{ 0x9bc4, 0, 8, 0x02 },
-	{ 0x9bc5, 0, 8, 0x00 },
-	{ 0x9bc6, 0, 8, 0x02 },
-	{ 0x9bc9, 0, 8, 0xf0 },
-	{ 0xd011, 0, 8, 0x3c },
-	{ 0xd012, 0, 2, 0x01 },
-	{ 0xd013, 0, 8, 0xf7 },
-	{ 0xd014, 0, 2, 0x02 },
-	{ 0xd040, 0, 8, 0x0b },
-	{ 0xd041, 0, 2, 0x02 },
-	{ 0xd042, 0, 8, 0x4d },
-	{ 0xd043, 0, 2, 0x00 },
-	{ 0xd045, 1, 1, 0x00 },
-	{ 0x9bcf, 0, 1, 0x01 },
-	{ 0xd045, 2, 1, 0x01 },
-	{ 0xd04f, 0, 8, 0x9a },
-	{ 0xd050, 0, 1, 0x01 },
-	{ 0xd051, 0, 8, 0x5a },
-	{ 0xd052, 0, 1, 0x01 },
-	{ 0xd053, 0, 8, 0x50 },
-	{ 0xd054, 0, 8, 0x46 },
-	{ 0x9bd7, 0, 8, 0x0a },
-	{ 0x9bd8, 0, 8, 0x14 },
-	{ 0x9bd9, 0, 8, 0x08 },
-	{ 0x9bd0, 0, 8, 0xa8 },
-	{ 0x9be4, 0, 8, 0x7f },
-	{ 0x9bbd, 0, 8, 0xa8 },
-	{ 0x9be2, 0, 8, 0x20 },
-	{ 0x9bee, 0, 1, 0x01 },
+/*
+ * NXP TDA18271 & TDA18218 tuner init
+ * AF9013_TUNER_TDA18271      0x9c
+ * AF9013_TUNER_TDA18218      0xb3
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_tda18271[] = {
+	{0x9bd5, 0xff, 0x01},
+	{0x9bd6, 0xff, 0x04},
+	{0xd1a0, 0x02, 0x02},
+	{0xd000, 0x01, 0x01},
+	{0xd000, 0x02, 0x00},
+	{0xd001, 0x02, 0x02},
+	{0xd001, 0x01, 0x00},
+	{0xd001, 0x20, 0x00},
+	{0xd002, 0x1f, 0x19},
+	{0xd003, 0x1f, 0x1a},
+	{0xd004, 0x1f, 0x19},
+	{0xd005, 0x1f, 0x1a},
+	{0xd00e, 0x1f, 0x10},
+	{0xd00f, 0x07, 0x04},
+	{0xd00f, 0x38, 0x28},
+	{0xd010, 0x07, 0x04},
+	{0xd010, 0x38, 0x28},
+	{0xd016, 0xf0, 0x30},
+	{0xd01f, 0x3f, 0x0a},
+	{0xd020, 0x3f, 0x0a},
+	{0x9bda, 0xff, 0x01},
+	{0x9be3, 0xff, 0x01},
+	{0xd1a0, 0x02, 0x00},
+	{0x9bbe, 0x01, 0x01},
+	{0x9bcc, 0x01, 0x01},
+	{0x9bb9, 0xff, 0x00},
+	{0x9bcd, 0xff, 0x18},
+	{0x9bff, 0xff, 0x2c},
+	{0xd015, 0xff, 0x46},
+	{0xd016, 0x01, 0x00},
+	{0xd044, 0xff, 0x46},
+	{0xd045, 0x01, 0x00},
+	{0xd008, 0xff, 0xdf},
+	{0xd009, 0x03, 0x02},
+	{0xd006, 0xff, 0x44},
+	{0xd007, 0x03, 0x01},
+	{0xd00c, 0xff, 0x00},
+	{0xd00d, 0x03, 0x02},
+	{0xd00a, 0xff, 0xf6},
+	{0xd00b, 0x03, 0x01},
+	{0x9bba, 0xff, 0xf9},
+	{0x9bc8, 0xff, 0xaa},
+	{0x9bc3, 0xff, 0xdf},
+	{0x9bc4, 0xff, 0x02},
+	{0x9bc5, 0xff, 0x00},
+	{0x9bc6, 0xff, 0x02},
+	{0x9bc9, 0xff, 0xf0},
+	{0xd011, 0xff, 0x3c},
+	{0xd012, 0x03, 0x01},
+	{0xd013, 0xff, 0xf7},
+	{0xd014, 0x03, 0x02},
+	{0xd040, 0xff, 0x0b},
+	{0xd041, 0x03, 0x02},
+	{0xd042, 0xff, 0x4d},
+	{0xd043, 0x03, 0x00},
+	{0xd045, 0x02, 0x00},
+	{0x9bcf, 0x01, 0x01},
+	{0xd045, 0x04, 0x04},
+	{0xd04f, 0xff, 0x9a},
+	{0xd050, 0x01, 0x01},
+	{0xd051, 0xff, 0x5a},
+	{0xd052, 0x01, 0x01},
+	{0xd053, 0xff, 0x50},
+	{0xd054, 0xff, 0x46},
+	{0x9bd7, 0xff, 0x0a},
+	{0x9bd8, 0xff, 0x14},
+	{0x9bd9, 0xff, 0x08},
+	{0x9bd0, 0xff, 0xa8},
+	{0x9be4, 0xff, 0x7f},
+	{0x9bbd, 0xff, 0xa8},
+	{0x9be2, 0xff, 0x20},
+	{0x9bee, 0x01, 0x01},
 };
 
 #endif /* AF9013_PRIV_H */

+ 104 - 105
drivers/staging/media/cxd2099/cxd2099.c → drivers/media/dvb-frontends/cxd2099.c

@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
+#include <linux/regmap.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
@@ -33,8 +34,9 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount);
 struct cxd {
 	struct dvb_ca_en50221 en;
 
-	struct i2c_adapter *i2c;
 	struct cxd2099_cfg cfg;
+	struct i2c_client *client;
+	struct regmap *regmap;
 
 	u8     regs[0x23];
 	u8     lastaddress;
@@ -56,69 +58,12 @@ struct cxd {
 	u8     wbuf[1028];
 };
 
-static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr,
-			 u8 reg, u8 data)
-{
-	u8 m[2] = {reg, data};
-	struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2};
-
-	if (i2c_transfer(adapter, &msg, 1) != 1) {
-		dev_err(&adapter->dev,
-			"Failed to write to I2C register %02x@%02x!\n",
-			reg, adr);
-		return -1;
-	}
-	return 0;
-}
-
-static int i2c_write(struct i2c_adapter *adapter, u8 adr,
-		     u8 *data, u16 len)
-{
-	struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len};
-
-	if (i2c_transfer(adapter, &msg, 1) != 1) {
-		dev_err(&adapter->dev, "Failed to write to I2C!\n");
-		return -1;
-	}
-	return 0;
-}
-
-static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr,
-			u8 reg, u8 *val)
-{
-	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
-				   .buf = &reg, .len = 1},
-				  {.addr = adr, .flags = I2C_M_RD,
-				   .buf = val, .len = 1} };
-
-	if (i2c_transfer(adapter, msgs, 2) != 2) {
-		dev_err(&adapter->dev, "error in %s()\n", __func__);
-		return -1;
-	}
-	return 0;
-}
-
-static int i2c_read(struct i2c_adapter *adapter, u8 adr,
-		    u8 reg, u8 *data, u16 n)
-{
-	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
-				   .buf = &reg, .len = 1},
-				  {.addr = adr, .flags = I2C_M_RD,
-				   .buf = data, .len = n} };
-
-	if (i2c_transfer(adapter, msgs, 2) != 2) {
-		dev_err(&adapter->dev, "error in %s()\n", __func__);
-		return -1;
-	}
-	return 0;
-}
-
 static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
 {
 	int status = 0;
 
 	if (ci->lastaddress != adr)
-		status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
+		status = regmap_write(ci->regmap, 0, adr);
 	if (!status) {
 		ci->lastaddress = adr;
 
@@ -127,7 +72,7 @@ static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
 
 			if (ci->cfg.max_i2c && len > ci->cfg.max_i2c)
 				len = ci->cfg.max_i2c;
-			status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, len);
+			status = regmap_raw_read(ci->regmap, 1, data, len);
 			if (status)
 				return status;
 			data += len;
@@ -145,64 +90,66 @@ static int read_reg(struct cxd *ci, u8 reg, u8 *val)
 static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
 {
 	int status;
-	u8 addr[3] = {2, address & 0xff, address >> 8};
+	u8 addr[2] = {address & 0xff, address >> 8};
 
-	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+	status = regmap_raw_write(ci->regmap, 2, addr, 2);
 	if (!status)
-		status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
+		status = regmap_raw_read(ci->regmap, 3, data, n);
 	return status;
 }
 
 static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
 {
 	int status;
-	u8 addr[3] = {2, address & 0xff, address >> 8};
+	u8 addr[2] = {address & 0xff, address >> 8};
 
-	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+	status = regmap_raw_write(ci->regmap, 2, addr, 2);
 	if (!status) {
-		u8 buf[256] = {3};
+		u8 buf[256];
 
-		memcpy(buf + 1, data, n);
-		status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
+		memcpy(buf, data, n);
+		status = regmap_raw_write(ci->regmap, 3, buf, n);
 	}
 	return status;
 }
 
-static int read_io(struct cxd *ci, u16 address, u8 *val)
+static int read_io(struct cxd *ci, u16 address, unsigned int *val)
 {
 	int status;
-	u8 addr[3] = {2, address & 0xff, address >> 8};
+	u8 addr[2] = {address & 0xff, address >> 8};
 
-	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+	status = regmap_raw_write(ci->regmap, 2, addr, 2);
 	if (!status)
-		status = i2c_read(ci->i2c, ci->cfg.adr, 3, val, 1);
+		status = regmap_read(ci->regmap, 3, val);
 	return status;
 }
 
 static int write_io(struct cxd *ci, u16 address, u8 val)
 {
 	int status;
-	u8 addr[3] = {2, address & 0xff, address >> 8};
-	u8 buf[2] = {3, val};
+	u8 addr[2] = {address & 0xff, address >> 8};
 
-	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+	status = regmap_raw_write(ci->regmap, 2, addr, 2);
 	if (!status)
-		status = i2c_write(ci->i2c, ci->cfg.adr, buf, 2);
+		status = regmap_write(ci->regmap, 3, val);
 	return status;
 }
 
 static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
 {
 	int status = 0;
+	unsigned int regval;
 
 	if (ci->lastaddress != reg)
-		status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg);
-	if (!status && reg >= 6 && reg <= 8 && mask != 0xff)
-		status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]);
+		status = regmap_write(ci->regmap, 0, reg);
+	if (!status && reg >= 6 && reg <= 8 && mask != 0xff) {
+		status = regmap_read(ci->regmap, 1, &regval);
+		ci->regs[reg] = regval;
+	}
 	ci->lastaddress = reg;
 	ci->regs[reg] = (ci->regs[reg] & (~mask)) | val;
 	if (!status)
-		status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]);
+		status = regmap_write(ci->regmap, 1, ci->regs[reg]);
 	if (reg == 0x20)
 		ci->regs[reg] &= 0x7f;
 	return status;
@@ -219,19 +166,18 @@ static int write_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
 	u8 *buf = ci->wbuf;
 
 	if (ci->lastaddress != adr)
-		status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
+		status = regmap_write(ci->regmap, 0, adr);
 	if (status)
 		return status;
 
 	ci->lastaddress = adr;
-	buf[0] = 1;
 	while (n) {
 		int len = n;
 
 		if (ci->cfg.max_i2c && (len + 1 > ci->cfg.max_i2c))
 			len = ci->cfg.max_i2c - 1;
-		memcpy(buf + 1, data, len);
-		status = i2c_write(ci->i2c, ci->cfg.adr, buf, len + 1);
+		memcpy(buf, data, len);
+		status = regmap_raw_write(ci->regmap, 1, buf, len);
 		if (status)
 			return status;
 		n -= len;
@@ -273,7 +219,7 @@ static void cam_mode(struct cxd *ci, int mode)
 		if (!ci->en.read_data)
 			return;
 		ci->write_busy = 0;
-		dev_info(&ci->i2c->dev, "enable cam buffer mode\n");
+		dev_info(&ci->client->dev, "enable cam buffer mode\n");
 		write_reg(ci, 0x0d, 0x00);
 		write_reg(ci, 0x0e, 0x01);
 		write_regm(ci, 0x08, 0x40, 0x40);
@@ -464,7 +410,7 @@ static int read_cam_control(struct dvb_ca_en50221 *ca,
 			    int slot, u8 address)
 {
 	struct cxd *ci = ca->data;
-	u8 val;
+	unsigned int val;
 
 	mutex_lock(&ci->lock);
 	set_mode(ci, 0);
@@ -518,7 +464,7 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
 {
 	struct cxd *ci = ca->data;
 
-	dev_dbg(&ci->i2c->dev, "%s\n", __func__);
+	dev_dbg(&ci->client->dev, "%s\n", __func__);
 	if (ci->cammode)
 		read_data(ca, slot, ci->rbuf, 0);
 	mutex_lock(&ci->lock);
@@ -577,7 +523,7 @@ static int campoll(struct cxd *ci)
 			if (ci->slot_stat) {
 				ci->slot_stat = 0;
 				write_regm(ci, 0x03, 0x00, 0x08);
-				dev_info(&ci->i2c->dev, "NO CAM\n");
+				dev_info(&ci->client->dev, "NO CAM\n");
 				ci->ready = 0;
 			}
 		}
@@ -660,26 +606,41 @@ static struct dvb_ca_en50221 en_templ = {
 	.write_data          = write_data,
 };
 
-struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
-				      void *priv,
-				      struct i2c_adapter *i2c)
+static int cxd2099_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
 {
 	struct cxd *ci;
-	u8 val;
+	struct cxd2099_cfg *cfg = client->dev.platform_data;
+	static const struct regmap_config rm_cfg = {
+		.reg_bits = 8,
+		.val_bits = 8,
+	};
+	unsigned int val;
+	int ret;
 
-	if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) {
-		dev_info(&i2c->dev, "No CXD2099AR detected at 0x%02x\n",
-			 cfg->adr);
-		return NULL;
+	ci = kzalloc(sizeof(*ci), GFP_KERNEL);
+	if (!ci) {
+		ret = -ENOMEM;
+		goto err;
 	}
 
-	ci = kzalloc(sizeof(*ci), GFP_KERNEL);
-	if (!ci)
-		return NULL;
+	ci->client = client;
+	memcpy(&ci->cfg, cfg, sizeof(ci->cfg));
+
+	ci->regmap = regmap_init_i2c(client, &rm_cfg);
+	if (IS_ERR(ci->regmap)) {
+		ret = PTR_ERR(ci->regmap);
+		goto err_kfree;
+	}
+
+	ret = regmap_read(ci->regmap, 0x00, &val);
+	if (ret < 0) {
+		dev_info(&client->dev, "No CXD2099AR detected at 0x%02x\n",
+			 client->addr);
+		goto err_rmexit;
+	}
 
 	mutex_init(&ci->lock);
-	ci->cfg = *cfg;
-	ci->i2c = i2c;
 	ci->lastaddress = 0xff;
 	ci->clk_reg_b = 0x4a;
 	ci->clk_reg_f = 0x1b;
@@ -687,18 +648,56 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
 	ci->en = en_templ;
 	ci->en.data = ci;
 	init(ci);
-	dev_info(&i2c->dev, "Attached CXD2099AR at 0x%02x\n", ci->cfg.adr);
+	dev_info(&client->dev, "Attached CXD2099AR at 0x%02x\n", client->addr);
+
+	*cfg->en = &ci->en;
 
 	if (!buffermode) {
 		ci->en.read_data = NULL;
 		ci->en.write_data = NULL;
 	} else {
-		dev_info(&i2c->dev, "Using CXD2099AR buffer mode");
+		dev_info(&client->dev, "Using CXD2099AR buffer mode");
 	}
 
-	return &ci->en;
+	i2c_set_clientdata(client, ci);
+
+	return 0;
+
+err_rmexit:
+	regmap_exit(ci->regmap);
+err_kfree:
+	kfree(ci);
+err:
+
+	return ret;
 }
-EXPORT_SYMBOL(cxd2099_attach);
+
+static int cxd2099_remove(struct i2c_client *client)
+{
+	struct cxd *ci = i2c_get_clientdata(client);
+
+	regmap_exit(ci->regmap);
+	kfree(ci);
+
+	return 0;
+}
+
+static const struct i2c_device_id cxd2099_id[] = {
+	{"cxd2099", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, cxd2099_id);
+
+static struct i2c_driver cxd2099_driver = {
+	.driver = {
+		.name	= "cxd2099",
+	},
+	.probe		= cxd2099_probe,
+	.remove		= cxd2099_remove,
+	.id_table	= cxd2099_id,
+};
+
+module_i2c_driver(cxd2099_driver);
 
 MODULE_DESCRIPTION("CXD2099AR Common Interface controller driver");
 MODULE_AUTHOR("Ralph Metzler");

+ 3 - 16
drivers/staging/media/cxd2099/cxd2099.h → drivers/media/dvb-frontends/cxd2099.h

@@ -20,26 +20,13 @@
 
 struct cxd2099_cfg {
 	u32 bitrate;
-	u8  adr;
 	u8  polarity;
 	u8  clock_mode;
 
 	u32 max_i2c;
-};
-
-#if defined(CONFIG_DVB_CXD2099) || \
-	(defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE))
-struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
-				      void *priv, struct i2c_adapter *i2c);
-#else
 
-static inline struct
-dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, void *priv,
-			       struct i2c_adapter *i2c)
-{
-	dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
-	return NULL;
-}
-#endif
+	/* ptr to DVB CA struct */
+	struct dvb_ca_en50221 **en;
+};
 
 #endif

+ 8 - 0
drivers/media/dvb-frontends/cxd2880/Kconfig

@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DVB_CXD2880
+	tristate "Sony CXD2880 DVB-T2/T tuner + demodulator"
+	depends on DVB_CORE && SPI
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y when you want to support this frontend.

+ 18 - 0
drivers/media/dvb-frontends/cxd2880/Makefile

@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+cxd2880-objs := cxd2880_common.o \
+		cxd2880_devio_spi.o \
+		cxd2880_integ.o \
+		cxd2880_io.o \
+		cxd2880_spi_device.o \
+		cxd2880_tnrdmd.o \
+		cxd2880_tnrdmd_dvbt2.o \
+		cxd2880_tnrdmd_dvbt2_mon.o \
+		cxd2880_tnrdmd_dvbt.o \
+		cxd2880_tnrdmd_dvbt_mon.o\
+		cxd2880_tnrdmd_mon.o\
+		cxd2880_top.o
+
+obj-$(CONFIG_DVB_CXD2880) += cxd2880.o
+
+ccflags-y += -Idrivers/media/dvb-frontends

+ 29 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880.h

@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_H
+#define CXD2880_H
+
+struct cxd2880_config {
+	struct spi_device *spi;
+	struct mutex *spi_mutex; /* For SPI access exclusive control */
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CXD2880)
+extern struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+					struct cxd2880_config *cfg);
+#else
+static inline struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+					struct cxd2880_config *cfg)
+{
+	pr_warn("%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_CXD2880 */
+
+#endif /* CXD2880_H */

+ 21 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_common.c

@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_common.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_common.h"
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen)
+{
+	if (!bitlen || bitlen >= 32)
+		return (int)value;
+
+	if (value & (u32)(1 << (bitlen - 1)))
+		return (int)(GENMASK(31, bitlen) | value);
+	else
+		return (int)(GENMASK(bitlen - 1, 0) & value);
+}

+ 19 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_common.h

@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_common.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_COMMON_H
+#define CXD2880_COMMON_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen);
+
+#endif

+ 129 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c

@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_devio_spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_devio_spi.h"
+
+#define BURST_WRITE_MAX 128
+
+static int cxd2880_io_spi_read_reg(struct cxd2880_io *io,
+				   enum cxd2880_io_tgt tgt,
+				   u8 sub_address, u8 *data,
+				   u32 size)
+{
+	int ret = 0;
+	struct cxd2880_spi *spi = NULL;
+	u8 send_data[6];
+	u8 *read_data_top = data;
+
+	if (!io || !io->if_object || !data)
+		return -EINVAL;
+
+	if (sub_address + size > 0x100)
+		return -EINVAL;
+
+	spi = io->if_object;
+
+	if (tgt == CXD2880_IO_TGT_SYS)
+		send_data[0] = 0x0b;
+	else
+		send_data[0] = 0x0a;
+
+	send_data[3] = 0;
+	send_data[4] = 0;
+	send_data[5] = 0;
+
+	while (size > 0) {
+		send_data[1] = sub_address;
+		if (size > 255)
+			send_data[2] = 255;
+		else
+			send_data[2] = size;
+
+		ret =
+		    spi->write_read(spi, send_data, sizeof(send_data),
+				    read_data_top, send_data[2]);
+		if (ret)
+			return ret;
+
+		sub_address += send_data[2];
+		read_data_top += send_data[2];
+		size -= send_data[2];
+	}
+
+	return ret;
+}
+
+static int cxd2880_io_spi_write_reg(struct cxd2880_io *io,
+				    enum cxd2880_io_tgt tgt,
+				    u8 sub_address,
+				    const u8 *data, u32 size)
+{
+	int ret = 0;
+	struct cxd2880_spi *spi = NULL;
+	u8 send_data[BURST_WRITE_MAX + 4];
+	const u8 *write_data_top = data;
+
+	if (!io || !io->if_object || !data)
+		return -EINVAL;
+
+	if (size > BURST_WRITE_MAX)
+		return -EINVAL;
+
+	if (sub_address + size > 0x100)
+		return -EINVAL;
+
+	spi = io->if_object;
+
+	if (tgt == CXD2880_IO_TGT_SYS)
+		send_data[0] = 0x0f;
+	else
+		send_data[0] = 0x0e;
+
+	while (size > 0) {
+		send_data[1] = sub_address;
+		if (size > 255)
+			send_data[2] = 255;
+		else
+			send_data[2] = size;
+
+		memcpy(&send_data[3], write_data_top, send_data[2]);
+
+		if (tgt == CXD2880_IO_TGT_SYS) {
+			send_data[3 + send_data[2]] = 0x00;
+			ret = spi->write(spi, send_data, send_data[2] + 4);
+		} else {
+			ret = spi->write(spi, send_data, send_data[2] + 3);
+		}
+		if (ret)
+			return ret;
+
+		sub_address += send_data[2];
+		write_data_top += send_data[2];
+		size -= send_data[2];
+	}
+
+	return ret;
+}
+
+int cxd2880_io_spi_create(struct cxd2880_io *io,
+			  struct cxd2880_spi *spi, u8 slave_select)
+{
+	if (!io || !spi)
+		return -EINVAL;
+
+	io->read_regs = cxd2880_io_spi_read_reg;
+	io->write_regs = cxd2880_io_spi_write_reg;
+	io->write_reg = cxd2880_io_common_write_one_reg;
+	io->if_object = spi;
+	io->i2c_address_sys = 0;
+	io->i2c_address_demod = 0;
+	io->slave_select = slave_select;
+
+	return 0;
+}

+ 23 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h

@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_devio_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DEVIO_SPI_H
+#define CXD2880_DEVIO_SPI_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_io.h"
+#include "cxd2880_spi.h"
+
+#include "cxd2880_tnrdmd.h"
+
+int cxd2880_io_spi_create(struct cxd2880_io *io,
+			  struct cxd2880_spi *spi,
+			  u8 slave_select);
+
+#endif

+ 29 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h

@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_dtv.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DTV related definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DTV_H
+#define CXD2880_DTV_H
+
+enum cxd2880_dtv_sys {
+	CXD2880_DTV_SYS_UNKNOWN,
+	CXD2880_DTV_SYS_DVBT,
+	CXD2880_DTV_SYS_DVBT2,
+	CXD2880_DTV_SYS_ANY
+};
+
+enum cxd2880_dtv_bandwidth {
+	CXD2880_DTV_BW_UNKNOWN = 0,
+	CXD2880_DTV_BW_1_7_MHZ = 1,
+	CXD2880_DTV_BW_5_MHZ = 5,
+	CXD2880_DTV_BW_6_MHZ = 6,
+	CXD2880_DTV_BW_7_MHZ = 7,
+	CXD2880_DTV_BW_8_MHZ = 8
+};
+
+#endif

+ 74 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h

@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T related definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DVBT_H
+#define CXD2880_DVBT_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt_constellation {
+	CXD2880_DVBT_CONSTELLATION_QPSK,
+	CXD2880_DVBT_CONSTELLATION_16QAM,
+	CXD2880_DVBT_CONSTELLATION_64QAM,
+	CXD2880_DVBT_CONSTELLATION_RESERVED_3
+};
+
+enum cxd2880_dvbt_hierarchy {
+	CXD2880_DVBT_HIERARCHY_NON,
+	CXD2880_DVBT_HIERARCHY_1,
+	CXD2880_DVBT_HIERARCHY_2,
+	CXD2880_DVBT_HIERARCHY_4
+};
+
+enum cxd2880_dvbt_coderate {
+	CXD2880_DVBT_CODERATE_1_2,
+	CXD2880_DVBT_CODERATE_2_3,
+	CXD2880_DVBT_CODERATE_3_4,
+	CXD2880_DVBT_CODERATE_5_6,
+	CXD2880_DVBT_CODERATE_7_8,
+	CXD2880_DVBT_CODERATE_RESERVED_5,
+	CXD2880_DVBT_CODERATE_RESERVED_6,
+	CXD2880_DVBT_CODERATE_RESERVED_7
+};
+
+enum cxd2880_dvbt_guard {
+	CXD2880_DVBT_GUARD_1_32,
+	CXD2880_DVBT_GUARD_1_16,
+	CXD2880_DVBT_GUARD_1_8,
+	CXD2880_DVBT_GUARD_1_4
+};
+
+enum cxd2880_dvbt_mode {
+	CXD2880_DVBT_MODE_2K,
+	CXD2880_DVBT_MODE_8K,
+	CXD2880_DVBT_MODE_RESERVED_2,
+	CXD2880_DVBT_MODE_RESERVED_3
+};
+
+enum cxd2880_dvbt_profile {
+	CXD2880_DVBT_PROFILE_HP = 0,
+	CXD2880_DVBT_PROFILE_LP
+};
+
+struct cxd2880_dvbt_tpsinfo {
+	enum cxd2880_dvbt_constellation constellation;
+	enum cxd2880_dvbt_hierarchy hierarchy;
+	enum cxd2880_dvbt_coderate rate_hp;
+	enum cxd2880_dvbt_coderate rate_lp;
+	enum cxd2880_dvbt_guard guard;
+	enum cxd2880_dvbt_mode mode;
+	u8 fnum;
+	u8 length_indicator;
+	u16 cell_id;
+	u8 cell_id_ok;
+	u8 reserved_even;
+	u8 reserved_odd;
+};
+
+#endif

+ 385 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h

@@ -0,0 +1,385 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 related definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DVBT2_H
+#define CXD2880_DVBT2_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt2_profile {
+	CXD2880_DVBT2_PROFILE_BASE,
+	CXD2880_DVBT2_PROFILE_LITE,
+	CXD2880_DVBT2_PROFILE_ANY
+};
+
+enum cxd2880_dvbt2_version {
+	CXD2880_DVBT2_V111,
+	CXD2880_DVBT2_V121,
+	CXD2880_DVBT2_V131
+};
+
+enum cxd2880_dvbt2_s1 {
+	CXD2880_DVBT2_S1_BASE_SISO = 0x00,
+	CXD2880_DVBT2_S1_BASE_MISO = 0x01,
+	CXD2880_DVBT2_S1_NON_DVBT2 = 0x02,
+	CXD2880_DVBT2_S1_LITE_SISO = 0x03,
+	CXD2880_DVBT2_S1_LITE_MISO = 0x04,
+	CXD2880_DVBT2_S1_RSVD3 = 0x05,
+	CXD2880_DVBT2_S1_RSVD4 = 0x06,
+	CXD2880_DVBT2_S1_RSVD5 = 0x07,
+	CXD2880_DVBT2_S1_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_base_s2 {
+	CXD2880_DVBT2_BASE_S2_M2K_G_ANY = 0x00,
+	CXD2880_DVBT2_BASE_S2_M8K_G_DVBT = 0x01,
+	CXD2880_DVBT2_BASE_S2_M4K_G_ANY = 0x02,
+	CXD2880_DVBT2_BASE_S2_M1K_G_ANY = 0x03,
+	CXD2880_DVBT2_BASE_S2_M16K_G_ANY = 0x04,
+	CXD2880_DVBT2_BASE_S2_M32K_G_DVBT = 0x05,
+	CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2 = 0x06,
+	CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2 = 0x07,
+	CXD2880_DVBT2_BASE_S2_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_lite_s2 {
+	CXD2880_DVBT2_LITE_S2_M2K_G_ANY = 0x00,
+	CXD2880_DVBT2_LITE_S2_M8K_G_DVBT = 0x01,
+	CXD2880_DVBT2_LITE_S2_M4K_G_ANY = 0x02,
+	CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2 = 0x03,
+	CXD2880_DVBT2_LITE_S2_M16K_G_DVBT = 0x04,
+	CXD2880_DVBT2_LITE_S2_RSVD1 = 0x05,
+	CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2 = 0x06,
+	CXD2880_DVBT2_LITE_S2_RSVD2 = 0x07,
+	CXD2880_DVBT2_LITE_S2_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_guard {
+	CXD2880_DVBT2_G1_32 = 0x00,
+	CXD2880_DVBT2_G1_16 = 0x01,
+	CXD2880_DVBT2_G1_8 = 0x02,
+	CXD2880_DVBT2_G1_4 = 0x03,
+	CXD2880_DVBT2_G1_128 = 0x04,
+	CXD2880_DVBT2_G19_128 = 0x05,
+	CXD2880_DVBT2_G19_256 = 0x06,
+	CXD2880_DVBT2_G_RSVD1 = 0x07,
+	CXD2880_DVBT2_G_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_mode {
+	CXD2880_DVBT2_M2K = 0x00,
+	CXD2880_DVBT2_M8K = 0x01,
+	CXD2880_DVBT2_M4K = 0x02,
+	CXD2880_DVBT2_M1K = 0x03,
+	CXD2880_DVBT2_M16K = 0x04,
+	CXD2880_DVBT2_M32K = 0x05,
+	CXD2880_DVBT2_M_RSVD1 = 0x06,
+	CXD2880_DVBT2_M_RSVD2 = 0x07
+};
+
+enum cxd2880_dvbt2_bw {
+	CXD2880_DVBT2_BW_8 = 0x00,
+	CXD2880_DVBT2_BW_7 = 0x01,
+	CXD2880_DVBT2_BW_6 = 0x02,
+	CXD2880_DVBT2_BW_5 = 0x03,
+	CXD2880_DVBT2_BW_10 = 0x04,
+	CXD2880_DVBT2_BW_1_7 = 0x05,
+	CXD2880_DVBT2_BW_RSVD1 = 0x06,
+	CXD2880_DVBT2_BW_RSVD2 = 0x07,
+	CXD2880_DVBT2_BW_RSVD3 = 0x08,
+	CXD2880_DVBT2_BW_RSVD4 = 0x09,
+	CXD2880_DVBT2_BW_RSVD5 = 0x0a,
+	CXD2880_DVBT2_BW_RSVD6 = 0x0b,
+	CXD2880_DVBT2_BW_RSVD7 = 0x0c,
+	CXD2880_DVBT2_BW_RSVD8 = 0x0d,
+	CXD2880_DVBT2_BW_RSVD9 = 0x0e,
+	CXD2880_DVBT2_BW_RSVD10 = 0x0f,
+	CXD2880_DVBT2_BW_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1pre_type {
+	CXD2880_DVBT2_L1PRE_TYPE_TS = 0x00,
+	CXD2880_DVBT2_L1PRE_TYPE_GS = 0x01,
+	CXD2880_DVBT2_L1PRE_TYPE_TS_GS = 0x02,
+	CXD2880_DVBT2_L1PRE_TYPE_RESERVED = 0x03,
+	CXD2880_DVBT2_L1PRE_TYPE_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_papr {
+	CXD2880_DVBT2_PAPR_0 = 0x00,
+	CXD2880_DVBT2_PAPR_1 = 0x01,
+	CXD2880_DVBT2_PAPR_2 = 0x02,
+	CXD2880_DVBT2_PAPR_3 = 0x03,
+	CXD2880_DVBT2_PAPR_RSVD1 = 0x04,
+	CXD2880_DVBT2_PAPR_RSVD2 = 0x05,
+	CXD2880_DVBT2_PAPR_RSVD3 = 0x06,
+	CXD2880_DVBT2_PAPR_RSVD4 = 0x07,
+	CXD2880_DVBT2_PAPR_RSVD5 = 0x08,
+	CXD2880_DVBT2_PAPR_RSVD6 = 0x09,
+	CXD2880_DVBT2_PAPR_RSVD7 = 0x0a,
+	CXD2880_DVBT2_PAPR_RSVD8 = 0x0b,
+	CXD2880_DVBT2_PAPR_RSVD9 = 0x0c,
+	CXD2880_DVBT2_PAPR_RSVD10 = 0x0d,
+	CXD2880_DVBT2_PAPR_RSVD11 = 0x0e,
+	CXD2880_DVBT2_PAPR_RSVD12 = 0x0f,
+	CXD2880_DVBT2_PAPR_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1post_constell {
+	CXD2880_DVBT2_L1POST_BPSK = 0x00,
+	CXD2880_DVBT2_L1POST_QPSK = 0x01,
+	CXD2880_DVBT2_L1POST_QAM16 = 0x02,
+	CXD2880_DVBT2_L1POST_QAM64 = 0x03,
+	CXD2880_DVBT2_L1POST_C_RSVD1 = 0x04,
+	CXD2880_DVBT2_L1POST_C_RSVD2 = 0x05,
+	CXD2880_DVBT2_L1POST_C_RSVD3 = 0x06,
+	CXD2880_DVBT2_L1POST_C_RSVD4 = 0x07,
+	CXD2880_DVBT2_L1POST_C_RSVD5 = 0x08,
+	CXD2880_DVBT2_L1POST_C_RSVD6 = 0x09,
+	CXD2880_DVBT2_L1POST_C_RSVD7 = 0x0a,
+	CXD2880_DVBT2_L1POST_C_RSVD8 = 0x0b,
+	CXD2880_DVBT2_L1POST_C_RSVD9 = 0x0c,
+	CXD2880_DVBT2_L1POST_C_RSVD10 = 0x0d,
+	CXD2880_DVBT2_L1POST_C_RSVD11 = 0x0e,
+	CXD2880_DVBT2_L1POST_C_RSVD12 = 0x0f,
+	CXD2880_DVBT2_L1POST_CONSTELL_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1post_cr {
+	CXD2880_DVBT2_L1POST_R1_2 = 0x00,
+	CXD2880_DVBT2_L1POST_R_RSVD1 = 0x01,
+	CXD2880_DVBT2_L1POST_R_RSVD2 = 0x02,
+	CXD2880_DVBT2_L1POST_R_RSVD3 = 0x03,
+	CXD2880_DVBT2_L1POST_R_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1post_fec_type {
+	CXD2880_DVBT2_L1POST_FEC_LDPC16K = 0x00,
+	CXD2880_DVBT2_L1POST_FEC_RSVD1 = 0x01,
+	CXD2880_DVBT2_L1POST_FEC_RSVD2 = 0x02,
+	CXD2880_DVBT2_L1POST_FEC_RSVD3 = 0x03,
+	CXD2880_DVBT2_L1POST_FEC_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_pp {
+	CXD2880_DVBT2_PP1 = 0x00,
+	CXD2880_DVBT2_PP2 = 0x01,
+	CXD2880_DVBT2_PP3 = 0x02,
+	CXD2880_DVBT2_PP4 = 0x03,
+	CXD2880_DVBT2_PP5 = 0x04,
+	CXD2880_DVBT2_PP6 = 0x05,
+	CXD2880_DVBT2_PP7 = 0x06,
+	CXD2880_DVBT2_PP8 = 0x07,
+	CXD2880_DVBT2_PP_RSVD1 = 0x08,
+	CXD2880_DVBT2_PP_RSVD2 = 0x09,
+	CXD2880_DVBT2_PP_RSVD3 = 0x0a,
+	CXD2880_DVBT2_PP_RSVD4 = 0x0b,
+	CXD2880_DVBT2_PP_RSVD5 = 0x0c,
+	CXD2880_DVBT2_PP_RSVD6 = 0x0d,
+	CXD2880_DVBT2_PP_RSVD7 = 0x0e,
+	CXD2880_DVBT2_PP_RSVD8 = 0x0f,
+	CXD2880_DVBT2_PP_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_code_rate {
+	CXD2880_DVBT2_R1_2 = 0x00,
+	CXD2880_DVBT2_R3_5 = 0x01,
+	CXD2880_DVBT2_R2_3 = 0x02,
+	CXD2880_DVBT2_R3_4 = 0x03,
+	CXD2880_DVBT2_R4_5 = 0x04,
+	CXD2880_DVBT2_R5_6 = 0x05,
+	CXD2880_DVBT2_R1_3 = 0x06,
+	CXD2880_DVBT2_R2_5 = 0x07,
+	CXD2880_DVBT2_PLP_CR_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_constell {
+	CXD2880_DVBT2_QPSK = 0x00,
+	CXD2880_DVBT2_QAM16 = 0x01,
+	CXD2880_DVBT2_QAM64 = 0x02,
+	CXD2880_DVBT2_QAM256 = 0x03,
+	CXD2880_DVBT2_CON_RSVD1 = 0x04,
+	CXD2880_DVBT2_CON_RSVD2 = 0x05,
+	CXD2880_DVBT2_CON_RSVD3 = 0x06,
+	CXD2880_DVBT2_CON_RSVD4 = 0x07,
+	CXD2880_DVBT2_CONSTELL_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_type {
+	CXD2880_DVBT2_PLP_TYPE_COMMON = 0x00,
+	CXD2880_DVBT2_PLP_TYPE_DATA1 = 0x01,
+	CXD2880_DVBT2_PLP_TYPE_DATA2 = 0x02,
+	CXD2880_DVBT2_PLP_TYPE_RSVD1 = 0x03,
+	CXD2880_DVBT2_PLP_TYPE_RSVD2 = 0x04,
+	CXD2880_DVBT2_PLP_TYPE_RSVD3 = 0x05,
+	CXD2880_DVBT2_PLP_TYPE_RSVD4 = 0x06,
+	CXD2880_DVBT2_PLP_TYPE_RSVD5 = 0x07,
+	CXD2880_DVBT2_PLP_TYPE_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_payload {
+	CXD2880_DVBT2_PLP_PAYLOAD_GFPS = 0x00,
+	CXD2880_DVBT2_PLP_PAYLOAD_GCS = 0x01,
+	CXD2880_DVBT2_PLP_PAYLOAD_GSE = 0x02,
+	CXD2880_DVBT2_PLP_PAYLOAD_TS = 0x03,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD1 = 0x04,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD2 = 0x05,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD3 = 0x06,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD4 = 0x07,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD5 = 0x08,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD6 = 0x09,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD7 = 0x0a,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD8 = 0x0b,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD9 = 0x0c,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD10 = 0x0d,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD11 = 0x0e,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD12 = 0x0f,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD13 = 0x10,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD14 = 0x11,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD15 = 0x12,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD16 = 0x13,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD17 = 0x14,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD18 = 0x15,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD19 = 0x16,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD20 = 0x17,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD21 = 0x18,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD22 = 0x19,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD23 = 0x1a,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD24 = 0x1b,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD25 = 0x1c,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD26 = 0x1d,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD27 = 0x1e,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD28 = 0x1f,
+	CXD2880_DVBT2_PLP_PAYLOAD_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_fec {
+	CXD2880_DVBT2_FEC_LDPC_16K = 0x00,
+	CXD2880_DVBT2_FEC_LDPC_64K = 0x01,
+	CXD2880_DVBT2_FEC_RSVD1 = 0x02,
+	CXD2880_DVBT2_FEC_RSVD2 = 0x03,
+	CXD2880_DVBT2_FEC_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_mode {
+	CXD2880_DVBT2_PLP_MODE_NOTSPECIFIED = 0x00,
+	CXD2880_DVBT2_PLP_MODE_NM = 0x01,
+	CXD2880_DVBT2_PLP_MODE_HEM = 0x02,
+	CXD2880_DVBT2_PLP_MODE_RESERVED = 0x03,
+	CXD2880_DVBT2_PLP_MODE_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_btype {
+	CXD2880_DVBT2_PLP_COMMON,
+	CXD2880_DVBT2_PLP_DATA
+};
+
+enum cxd2880_dvbt2_stream {
+	CXD2880_DVBT2_STREAM_GENERIC_PACKETIZED = 0x00,
+	CXD2880_DVBT2_STREAM_GENERIC_CONTINUOUS = 0x01,
+	CXD2880_DVBT2_STREAM_GENERIC_ENCAPSULATED = 0x02,
+	CXD2880_DVBT2_STREAM_TRANSPORT = 0x03,
+	CXD2880_DVBT2_STREAM_UNKNOWN = 0xff
+};
+
+struct cxd2880_dvbt2_l1pre {
+	enum cxd2880_dvbt2_l1pre_type type;
+	u8 bw_ext;
+	enum cxd2880_dvbt2_s1 s1;
+	u8 s2;
+	u8 mixed;
+	enum cxd2880_dvbt2_mode fft_mode;
+	u8 l1_rep;
+	enum cxd2880_dvbt2_guard gi;
+	enum cxd2880_dvbt2_papr papr;
+	enum cxd2880_dvbt2_l1post_constell mod;
+	enum cxd2880_dvbt2_l1post_cr cr;
+	enum cxd2880_dvbt2_l1post_fec_type fec;
+	u32 l1_post_size;
+	u32 l1_post_info_size;
+	enum cxd2880_dvbt2_pp pp;
+	u8 tx_id_availability;
+	u16 cell_id;
+	u16 network_id;
+	u16 sys_id;
+	u8 num_frames;
+	u16 num_symbols;
+	u8 regen;
+	u8 post_ext;
+	u8 num_rf_freqs;
+	u8 rf_idx;
+	enum cxd2880_dvbt2_version t2_version;
+	u8 l1_post_scrambled;
+	u8 t2_base_lite;
+	u32 crc32;
+};
+
+struct cxd2880_dvbt2_plp {
+	u8 id;
+	enum cxd2880_dvbt2_plp_type type;
+	enum cxd2880_dvbt2_plp_payload payload;
+	u8 ff;
+	u8 first_rf_idx;
+	u8 first_frm_idx;
+	u8 group_id;
+	enum cxd2880_dvbt2_plp_constell constell;
+	enum cxd2880_dvbt2_plp_code_rate plp_cr;
+	u8 rot;
+	enum cxd2880_dvbt2_plp_fec fec;
+	u16 num_blocks_max;
+	u8 frm_int;
+	u8 til_len;
+	u8 til_type;
+	u8 in_band_a_flag;
+	u8 in_band_b_flag;
+	u16 rsvd;
+	enum cxd2880_dvbt2_plp_mode plp_mode;
+	u8 static_flag;
+	u8 static_padding_flag;
+};
+
+struct cxd2880_dvbt2_l1post {
+	u16 sub_slices_per_frame;
+	u8 num_plps;
+	u8 num_aux;
+	u8 aux_cfg_rfu;
+	u8 rf_idx;
+	u32 freq;
+	u8 fef_type;
+	u32 fef_length;
+	u8 fef_intvl;
+};
+
+struct cxd2880_dvbt2_ofdm {
+	u8 mixed;
+	u8 is_miso;
+	enum cxd2880_dvbt2_mode mode;
+	enum cxd2880_dvbt2_guard gi;
+	enum cxd2880_dvbt2_pp pp;
+	u8 bw_ext;
+	enum cxd2880_dvbt2_papr papr;
+	u16 num_symbols;
+};
+
+struct cxd2880_dvbt2_bbheader {
+	enum cxd2880_dvbt2_stream stream_input;
+	u8 is_single_input_stream;
+	u8 is_constant_coding_modulation;
+	u8 issy_indicator;
+	u8 null_packet_deletion;
+	u8 ext;
+	u8 input_stream_identifier;
+	u16 user_packet_length;
+	u16 data_field_length;
+	u8 sync_byte;
+	u32 issy;
+	enum cxd2880_dvbt2_plp_mode plp_mode;
+};
+
+#endif

+ 72 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c

@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_integ.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <linux/ktime.h>
+#include <linux/errno.h>
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_integ.h"
+
+int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	int ret;
+	ktime_t start;
+	u8 cpu_task_completed = 0;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_init1(tnr_dmd);
+	if (ret)
+		return ret;
+
+	start = ktime_get();
+
+	while (1) {
+		ret =
+		    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+						     &cpu_task_completed);
+		if (ret)
+			return ret;
+
+		if (cpu_task_completed)
+			break;
+
+		if (ktime_to_ms(ktime_sub(ktime_get(), start)) >
+					CXD2880_TNRDMD_WAIT_INIT_TIMEOUT)
+			return -ETIMEDOUT;
+
+		usleep_range(CXD2880_TNRDMD_WAIT_INIT_INTVL,
+			     CXD2880_TNRDMD_WAIT_INIT_INTVL + 1000);
+	}
+
+	return cxd2880_tnrdmd_init2(tnr_dmd);
+}
+
+int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	atomic_set(&tnr_dmd->cancel, 1);
+
+	return 0;
+}
+
+int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (atomic_read(&tnr_dmd->cancel) != 0)
+		return -ECANCELED;
+
+	return 0;
+}

+ 27 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h

@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_integ.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_INTEG_H
+#define CXD2880_INTEG_H
+
+#include "cxd2880_tnrdmd.h"
+
+#define CXD2880_TNRDMD_WAIT_INIT_TIMEOUT	500
+#define CXD2880_TNRDMD_WAIT_INIT_INTVL	10
+
+#define CXD2880_TNRDMD_WAIT_AGC_STABLE		100
+
+int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd
+				     *tnr_dmd);
+
+#endif

+ 66 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_io.c

@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_io.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_io.h"
+
+int cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+				    enum cxd2880_io_tgt tgt,
+				    u8 sub_address, u8 data)
+{
+	if (!io)
+		return -EINVAL;
+
+	return io->write_regs(io, tgt, sub_address, &data, 1);
+}
+
+int cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+			    enum cxd2880_io_tgt tgt,
+			    u8 sub_address, u8 data, u8 mask)
+{
+	int ret;
+
+	if (!io)
+		return -EINVAL;
+
+	if (mask == 0x00)
+		return 0;
+
+	if (mask != 0xff) {
+		u8 rdata = 0x00;
+
+		ret = io->read_regs(io, tgt, sub_address, &rdata, 1);
+		if (ret)
+			return ret;
+
+		data = (data & mask) | (rdata & (mask ^ 0xff));
+	}
+
+	return io->write_reg(io, tgt, sub_address, data);
+}
+
+int cxd2880_io_write_multi_regs(struct cxd2880_io *io,
+			     enum cxd2880_io_tgt tgt,
+			     const struct cxd2880_reg_value reg_value[],
+			     u8 size)
+{
+	int ret;
+	int i;
+
+	if (!io)
+		return -EINVAL;
+
+	for (i = 0; i < size ; i++) {
+		ret = io->write_reg(io, tgt, reg_value[i].addr,
+				    reg_value[i].value);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}

+ 54 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_io.h

@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_io.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_IO_H
+#define CXD2880_IO_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_io_tgt {
+	CXD2880_IO_TGT_SYS,
+	CXD2880_IO_TGT_DMD
+};
+
+struct cxd2880_reg_value {
+	u8 addr;
+	u8 value;
+};
+
+struct cxd2880_io {
+	int (*read_regs)(struct cxd2880_io *io,
+			 enum cxd2880_io_tgt tgt, u8 sub_address,
+			 u8 *data, u32 size);
+	int (*write_regs)(struct cxd2880_io *io,
+			  enum cxd2880_io_tgt tgt, u8 sub_address,
+			  const u8 *data, u32 size);
+	int (*write_reg)(struct cxd2880_io *io,
+			 enum cxd2880_io_tgt tgt, u8 sub_address,
+			 u8 data);
+	void *if_object;
+	u8 i2c_address_sys;
+	u8 i2c_address_demod;
+	u8 slave_select;
+	void *user;
+};
+
+int cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+				    enum cxd2880_io_tgt tgt,
+				    u8 sub_address, u8 data);
+
+int cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+			    enum cxd2880_io_tgt tgt,
+			    u8 sub_address, u8 data, u8 mask);
+
+int cxd2880_io_write_multi_regs(struct cxd2880_io *io,
+				enum cxd2880_io_tgt tgt,
+				const struct cxd2880_reg_value reg_value[],
+				u8 size);
+#endif

+ 34 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h

@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_SPI_H
+#define CXD2880_SPI_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_spi_mode {
+	CXD2880_SPI_MODE_0,
+	CXD2880_SPI_MODE_1,
+	CXD2880_SPI_MODE_2,
+	CXD2880_SPI_MODE_3
+};
+
+struct cxd2880_spi {
+	int (*read)(struct cxd2880_spi *spi, u8 *data,
+		    u32 size);
+	int (*write)(struct cxd2880_spi *spi, const u8 *data,
+		     u32 size);
+	int (*write_read)(struct cxd2880_spi *spi,
+			  const u8 *tx_data, u32 tx_size,
+			  u8 *rx_data, u32 rx_size);
+	u32 flags;
+	void *user;
+};
+
+#endif

+ 113 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c

@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_spi_device.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <linux/spi/spi.h>
+
+#include "cxd2880_spi_device.h"
+
+static int cxd2880_spi_device_write(struct cxd2880_spi *spi,
+				    const u8 *data, u32 size)
+{
+	struct cxd2880_spi_device *spi_device = NULL;
+	struct spi_message msg;
+	struct spi_transfer tx;
+	int result = 0;
+
+	if (!spi || !spi->user || !data || size == 0)
+		return -EINVAL;
+
+	spi_device = spi->user;
+
+	memset(&tx, 0, sizeof(tx));
+	tx.tx_buf = data;
+	tx.len = size;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&tx, &msg);
+	result = spi_sync(spi_device->spi, &msg);
+
+	if (result < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int cxd2880_spi_device_write_read(struct cxd2880_spi *spi,
+					 const u8 *tx_data,
+					 u32 tx_size,
+					 u8 *rx_data,
+					 u32 rx_size)
+{
+	struct cxd2880_spi_device *spi_device = NULL;
+	int result = 0;
+
+	if (!spi || !spi->user || !tx_data ||
+	    !tx_size || !rx_data || !rx_size)
+		return -EINVAL;
+
+	spi_device = spi->user;
+
+	result = spi_write_then_read(spi_device->spi, tx_data,
+				     tx_size, rx_data, rx_size);
+	if (result < 0)
+		return -EIO;
+
+	return 0;
+}
+
+int
+cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+			      enum cxd2880_spi_mode mode,
+			      u32 speed_hz)
+{
+	int result = 0;
+	struct spi_device *spi = spi_device->spi;
+
+	switch (mode) {
+	case CXD2880_SPI_MODE_0:
+		spi->mode = SPI_MODE_0;
+		break;
+	case CXD2880_SPI_MODE_1:
+		spi->mode = SPI_MODE_1;
+		break;
+	case CXD2880_SPI_MODE_2:
+		spi->mode = SPI_MODE_2;
+		break;
+	case CXD2880_SPI_MODE_3:
+		spi->mode = SPI_MODE_3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spi->max_speed_hz = speed_hz;
+	spi->bits_per_word = 8;
+	result = spi_setup(spi);
+	if (result != 0) {
+		pr_err("spi_setup failed %d\n", result);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+				  struct cxd2880_spi_device *spi_device)
+{
+	if (!spi || !spi_device)
+		return -EINVAL;
+
+	spi->read = NULL;
+	spi->write = cxd2880_spi_device_write;
+	spi->write_read = cxd2880_spi_device_write_read;
+	spi->flags = 0;
+	spi->user = spi_device;
+
+	return 0;
+}

+ 26 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h

@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_spi_device.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_SPI_DEVICE_H
+#define CXD2880_SPI_DEVICE_H
+
+#include "cxd2880_spi.h"
+
+struct cxd2880_spi_device {
+	struct spi_device *spi;
+};
+
+int cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+				  enum cxd2880_spi_mode mode,
+				  u32 speedHz);
+
+int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+				  struct cxd2880_spi_device *spi_device);
+
+#endif /* CXD2880_SPI_DEVICE_H */

+ 3519 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c

@@ -0,0 +1,3519 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <media/dvb_frontend.h>
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+
+static const struct cxd2880_reg_value p_init1_seq[] = {
+	{0x11, 0x16}, {0x00, 0x10},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq1[] = {
+	{0x4f, 0x18}, {0x61, 0x00}, {0x71, 0x00}, {0x9d, 0x01},
+	{0x7d, 0x02}, {0x8f, 0x01}, {0x8b, 0xc6}, {0x9a, 0x03},
+	{0x1c, 0x00},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq2[] = {
+	{0xb9, 0x07}, {0x33, 0x01}, {0xc1, 0x01}, {0xc4, 0x1e},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq3[] = {
+	{0x00, 0x10}, {0x51, 0x01}, {0xc5, 0x07}, {0x00, 0x11},
+	{0x70, 0xe9}, {0x76, 0x0a}, {0x78, 0x32}, {0x7a, 0x46},
+	{0x7c, 0x86}, {0x7e, 0xa4}, {0x00, 0x10}, {0xe1, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq4[] = {
+	{0x15, 0x00}, {0x00, 0x16}
+};
+
+static const struct cxd2880_reg_value rf_init1_seq5[] = {
+	{0x00, 0x00}, {0x25, 0x00}
+};
+
+static const struct cxd2880_reg_value rf_init1_seq6[] = {
+	{0x02, 0x00}, {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1},
+	{0x8f, 0x16}, {0x67, 0x60}, {0x6a, 0x0f}, {0x6c, 0x17}
+};
+
+static const struct cxd2880_reg_value rf_init1_seq7[] = {
+	{0x00, 0xe2}, {0x41, 0xa0}, {0x4b, 0x68}, {0x00, 0x00},
+	{0x21, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq8[] = {
+	{0x00, 0x10}, {0x25, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq9[] = {
+	{0x00, 0x10}, {0x14, 0x01}, {0x00, 0x00}, {0x26, 0x00},
+};
+
+static const struct cxd2880_reg_value rf_init2_seq1[] = {
+	{0x00, 0x14}, {0x1b, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init2_seq2[] = {
+	{0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1}, {0xd3, 0x00},
+	{0x00, 0x00}, {0x21, 0x00},
+};
+
+static const struct cxd2880_reg_value x_tune1_seq1[] = {
+	{0x00, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune1_seq2[] = {
+	{0x62, 0x00}, {0x00, 0x15},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq1[] = {
+	{0x00, 0x1a}, {0x29, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq2[] = {
+	{0x62, 0x01}, {0x00, 0x11}, {0x2d, 0x00}, {0x2f, 0x00},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq3[] = {
+	{0x00, 0x00}, {0x10, 0x00}, {0x21, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq4[] = {
+	{0x00, 0xe1}, {0x8a, 0x87},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq5[] = {
+	{0x00, 0x00}, {0x21, 0x00},
+};
+
+static const struct cxd2880_reg_value x_tune3_seq[] = {
+	{0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0xa0},
+	{0x00, 0x00}, {0x21, 0x00}, {0xfe, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune4_seq[] = {
+	{0x00, 0x00}, {0xfe, 0x01},
+};
+
+static const struct cxd2880_reg_value x_sleep1_seq[] = {
+	{0x00, 0x00}, {0x57, 0x03},
+};
+
+static const struct cxd2880_reg_value x_sleep2_seq1[] = {
+	{0x00, 0x2d}, {0xb1, 0x01},
+};
+
+static const struct cxd2880_reg_value x_sleep2_seq2[] = {
+	{0x00, 0x10}, {0xf4, 0x00}, {0xf3, 0x00}, {0xf2, 0x00},
+	{0xf1, 0x00}, {0xf0, 0x00}, {0xef, 0x00},
+};
+
+static const struct cxd2880_reg_value x_sleep3_seq[] = {
+	{0x00, 0x00}, {0xfd, 0x00},
+};
+
+static const struct cxd2880_reg_value x_sleep4_seq[] = {
+	{0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0x00},
+	{0x00, 0x00}, {0x21, 0x00},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq1[] = {
+	{0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01},
+	{0x26, 0x01},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq2[] = {
+	{0x00, 0x00}, {0x10, 0x00},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq3[] = {
+	{0x00, 0x00}, {0x27, 0x00}, {0x22, 0x01},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq4[] = {
+	{0x00, 0x00}, {0x27, 0x01},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq5[] = {
+	{0x00, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq1[] = {
+	{0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq2[] = {
+	{0x00, 0x00}, {0x10, 0x00},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq3[] = {
+	{0x00, 0x00}, {0x27, 0x00}, {0x25, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq4[] = {
+	{0x00, 0x00}, {0x2a, 0x00},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq5[] = {
+	{0x00, 0x00}, {0x25, 0x00},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq6[] = {
+	{0x00, 0x00}, {0x27, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq7[] = {
+	{0x00, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value set_ts_pin_seq[] = {
+	{0x50, 0x3f}, {0x52, 0x1f},
+
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq1[] = {
+	{0x00, 0x00}, {0x52, 0x00},
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq2[] = {
+	{0x00, 0x00}, {0xc3, 0x00},
+
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq3[] = {
+	{0x00, 0x00}, {0xc3, 0x01},
+
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq4[] = {
+	{0x00, 0x00}, {0x52, 0x1f},
+
+};
+
+static int p_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data = 0;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE ||
+	    tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		switch (tnr_dmd->create_param.ts_output_if) {
+		case CXD2880_TNRDMD_TSOUT_IF_TS:
+			data = 0x00;
+			break;
+		case CXD2880_TNRDMD_TSOUT_IF_SPI:
+			data = 0x01;
+			break;
+		case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+			data = 0x02;
+			break;
+		default:
+			return -EINVAL;
+		}
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS,
+					     0x10, data);
+		if (ret)
+			return ret;
+	}
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  p_init1_seq,
+					  ARRAY_SIZE(p_init1_seq));
+	if (ret)
+		return ret;
+
+	switch (tnr_dmd->chip_id) {
+	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+		data = 0x1a;
+		break;
+	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+		data = 0x16;
+		break;
+	default:
+		return -ENOTTY;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x10, data);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->create_param.en_internal_ldo)
+		data = 0x01;
+	else
+		data = 0x00;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x11, data);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x13, data);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x12, data);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	switch (tnr_dmd->chip_id) {
+	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+		data = 0x01;
+		break;
+	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+		data = 0x00;
+		break;
+	default:
+		return -ENOTTY;
+	}
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x69, data);
+}
+
+static int p_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[6] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+	data[0] = tnr_dmd->create_param.xosc_cap;
+	data[1] = tnr_dmd->create_param.xosc_i;
+	switch (tnr_dmd->create_param.xtal_share_type) {
+	case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+		data[2] = 0x01;
+		data[3] = 0x00;
+		break;
+	case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+		data[2] = 0x00;
+		data[3] = 0x00;
+		break;
+	case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+		data[2] = 0x01;
+		data[3] = 0x01;
+		break;
+	case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+		data[2] = 0x00;
+		data[3] = 0x01;
+		break;
+	default:
+		return -EINVAL;
+	}
+	data[4] = 0x06;
+	data[5] = 0x00;
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_SYS,
+				       0x13, data, 6);
+}
+
+static int p_init3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[2] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	switch (tnr_dmd->diver_mode) {
+	case CXD2880_TNRDMD_DIVERMODE_SINGLE:
+		data[0] = 0x00;
+		break;
+	case CXD2880_TNRDMD_DIVERMODE_MAIN:
+		data[0] = 0x03;
+		break;
+	case CXD2880_TNRDMD_DIVERMODE_SUB:
+		data[0] = 0x02;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	data[1] = 0x01;
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_SYS,
+				       0x1f, data, 2);
+}
+
+static int rf_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[8] = { 0 };
+	static const u8 rf_init1_cdata1[40] = {
+		0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+		0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03,
+		0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+		0x02, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02,
+		0x02, 0x03, 0x04, 0x04, 0x04
+	};
+
+	static const u8 rf_init1_cdata2[5] = {0xff, 0x00, 0x00, 0x00, 0x00};
+	static const u8 rf_init1_cdata3[80] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+		0x01, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00,
+		0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00,
+		0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x09,
+		0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00,
+		0x0d, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f,
+		0x00, 0x10, 0x00, 0x79, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01,
+		0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
+		0x04, 0x00, 0x04, 0x00, 0x06, 0x00, 0x05,
+		0x00, 0x07, 0x00, 0x07, 0x00, 0x08, 0x00,
+		0x0a, 0x03, 0xe0
+	};
+
+	static const u8 rf_init1_cdata4[8] = {
+		0x20, 0x20, 0x30, 0x41, 0x50, 0x5f, 0x6f, 0x80
+	};
+
+	static const u8 rf_init1_cdata5[50] = {
+		0x00, 0x09, 0x00, 0x08, 0x00, 0x07, 0x00,
+		0x06, 0x00, 0x05, 0x00, 0x03, 0x00, 0x02,
+		0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
+		0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c,
+		0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0f, 0x00,
+		0x0e, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f,
+		0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f, 0x00,
+		0x0e
+	};
+
+	u8 addr = 0;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+	data[0] = 0x01;
+	data[1] = 0x00;
+	data[2] = 0x01;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x21, data, 3);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+	data[0] = 0x01;
+	data[1] = 0x01;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x17, data, 2);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->create_param.stationary_use) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS,
+					     0x1a, 0x06);
+		if (ret)
+			return ret;
+	}
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  rf_init1_seq1,
+					  ARRAY_SIZE(rf_init1_seq1));
+	if (ret)
+		return ret;
+
+	data[0] = 0x00;
+	if (tnr_dmd->create_param.is_cxd2881gg &&
+	    tnr_dmd->create_param.xtal_share_type ==
+		CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
+		data[1] = 0x00;
+	else
+		data[1] = 0x1f;
+	data[2] = 0x0a;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xb5, data, 3);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  rf_init1_seq2,
+					  ARRAY_SIZE(rf_init1_seq2));
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) {
+		data[0] = 0x34;
+		data[1] = 0x2c;
+	} else {
+		data[0] = 0x2f;
+		data[1] = 0x25;
+	}
+	data[2] = 0x15;
+	data[3] = 0x19;
+	data[4] = 0x1b;
+	data[5] = 0x15;
+	data[6] = 0x19;
+	data[7] = 0x1b;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xd9, data, 8);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x11);
+	if (ret)
+		return ret;
+	data[0] = 0x6c;
+	data[1] = 0x10;
+	data[2] = 0xa6;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x44, data, 3);
+	if (ret)
+		return ret;
+	data[0] = 0x16;
+	data[1] = 0xa8;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x50, data, 2);
+	if (ret)
+		return ret;
+	data[0] = 0x00;
+	data[1] = 0x22;
+	data[2] = 0x00;
+	data[3] = 0x88;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x62, data, 4);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x74, 0x75);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x7f, rf_init1_cdata1, 40);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x16);
+	if (ret)
+		return ret;
+	data[0] = 0x00;
+	data[1] = 0x71;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x10, data, 2);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x23, 0x89);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x27, rf_init1_cdata2, 5);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x3a, rf_init1_cdata3, 80);
+	if (ret)
+		return ret;
+
+	data[0] = 0x03;
+	data[1] = 0xe0;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xbc, data, 2);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  rf_init1_seq3,
+					  ARRAY_SIZE(rf_init1_seq3));
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->create_param.stationary_use) {
+		data[0] = 0x06;
+		data[1] = 0x07;
+		data[2] = 0x1a;
+	} else {
+		data[0] = 0x00;
+		data[1] = 0x08;
+		data[2] = 0x19;
+	}
+	data[3] = 0x0e;
+	data[4] = 0x09;
+	data[5] = 0x0e;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x12);
+	if (ret)
+		return ret;
+	for (addr = 0x10; addr < 0x9f; addr += 6) {
+		if (tnr_dmd->lna_thrs_tbl_air) {
+			u8 idx = 0;
+
+			idx = (addr - 0x10) / 6;
+			data[0] =
+			    tnr_dmd->lna_thrs_tbl_air->thrs[idx].off_on;
+			data[1] =
+			    tnr_dmd->lna_thrs_tbl_air->thrs[idx].on_off;
+		}
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_SYS,
+					      addr, data, 6);
+		if (ret)
+			return ret;
+	}
+
+	data[0] = 0x00;
+	data[1] = 0x08;
+	if (tnr_dmd->create_param.stationary_use)
+		data[2] = 0x1a;
+	else
+		data[2] = 0x19;
+	data[3] = 0x0e;
+	data[4] = 0x09;
+	data[5] = 0x0e;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x13);
+	if (ret)
+		return ret;
+	for (addr = 0x10; addr < 0xcf; addr += 6) {
+		if (tnr_dmd->lna_thrs_tbl_cable) {
+			u8 idx = 0;
+
+			idx = (addr - 0x10) / 6;
+			data[0] =
+			    tnr_dmd->lna_thrs_tbl_cable->thrs[idx].off_on;
+			data[1] =
+			    tnr_dmd->lna_thrs_tbl_cable->thrs[idx].on_off;
+		}
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_SYS,
+					      addr, data, 6);
+		if (ret)
+			return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x11);
+	if (ret)
+		return ret;
+	data[0] = 0x08;
+	data[1] = 0x09;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xbd, data, 2);
+	if (ret)
+		return ret;
+	data[0] = 0x08;
+	data[1] = 0x09;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xc4, data, 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xc9, rf_init1_cdata4, 8);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x14);
+	if (ret)
+		return ret;
+	data[0] = 0x15;
+	data[1] = 0x18;
+	data[2] = 0x00;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x10, data, 3);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  rf_init1_seq4,
+					  ARRAY_SIZE(rf_init1_seq4));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x12, rf_init1_cdata5, 50);
+	if (ret)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x0a);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x10, data, 1);
+	if (ret)
+		return ret;
+	if ((data[0] & 0x01) == 0x00)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  rf_init1_seq5,
+					  ARRAY_SIZE(rf_init1_seq5));
+	if (ret)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x0a);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x11, data, 1);
+	if (ret)
+		return ret;
+	if ((data[0] & 0x01) == 0x00)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  rf_init1_seq6,
+					  ARRAY_SIZE(rf_init1_seq6));
+	if (ret)
+		return ret;
+
+	data[0] = 0x00;
+	data[1] = 0xfe;
+	data[2] = 0xee;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x6e, data, 3);
+	if (ret)
+		return ret;
+	data[0] = 0xa1;
+	data[1] = 0x8b;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x8d, data, 2);
+	if (ret)
+		return ret;
+	data[0] = 0x08;
+	data[1] = 0x09;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x77, data, 2);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->create_param.stationary_use) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x80, 0xaa);
+		if (ret)
+			return ret;
+	}
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  rf_init1_seq7,
+					  ARRAY_SIZE(rf_init1_seq7));
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  rf_init1_seq8,
+					  ARRAY_SIZE(rf_init1_seq8));
+	if (ret)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x1a);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x10, data, 1);
+	if (ret)
+		return ret;
+	if ((data[0] & 0x01) == 0x00)
+		return -EINVAL;
+
+	return cxd2880_io_write_multi_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS,
+					   rf_init1_seq9,
+					   ARRAY_SIZE(rf_init1_seq9));
+}
+
+static int rf_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[5] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+	data[0] = 0x40;
+	data[1] = 0x40;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xea, data, 2);
+	if (ret)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	data[0] = 0x00;
+	if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X)
+		data[1] = 0x00;
+	else
+		data[1] = 0x01;
+	data[2] = 0x01;
+	data[3] = 0x03;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x30, data, 4);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  rf_init2_seq1,
+					  ARRAY_SIZE(rf_init2_seq1));
+	if (ret)
+		return ret;
+
+	return cxd2880_io_write_multi_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD,
+					   rf_init2_seq2,
+					   ARRAY_SIZE(rf_init2_seq2));
+}
+
+static int x_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+		   enum cxd2880_dtv_sys sys, u32 freq_khz,
+		   enum cxd2880_dtv_bandwidth bandwidth,
+		   u8 is_cable, int shift_frequency_khz)
+{
+	u8 data[11] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  x_tune1_seq1,
+					  ARRAY_SIZE(x_tune1_seq1));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	data[2] = 0x0e;
+	data[4] = 0x03;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xe7, data, 5);
+	if (ret)
+		return ret;
+
+	data[0] = 0x1f;
+	data[1] = 0x80;
+	data[2] = 0x18;
+	data[3] = 0x00;
+	data[4] = 0x07;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xe7, data, 5);
+	if (ret)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	data[0] = 0x72;
+	data[1] = 0x81;
+	data[3] = 0x1d;
+	data[4] = 0x6f;
+	data[5] = 0x7e;
+	data[7] = 0x1c;
+	switch (sys) {
+	case CXD2880_DTV_SYS_DVBT:
+		data[2] = 0x94;
+		data[6] = 0x91;
+		break;
+	case CXD2880_DTV_SYS_DVBT2:
+		data[2] = 0x96;
+		data[6] = 0x93;
+		break;
+	default:
+		return -EINVAL;
+	}
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x44, data, 8);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  x_tune1_seq2,
+					  ARRAY_SIZE(x_tune1_seq2));
+	if (ret)
+		return ret;
+
+	data[0] = 0x03;
+	data[1] = 0xe2;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x1e, data, 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	data[0] = is_cable ? 0x01 : 0x00;
+	data[1] = 0x00;
+	data[2] = 0x6b;
+	data[3] = 0x4d;
+
+	switch (bandwidth) {
+	case CXD2880_DTV_BW_1_7_MHZ:
+		data[4] = 0x03;
+		break;
+	case CXD2880_DTV_BW_5_MHZ:
+	case CXD2880_DTV_BW_6_MHZ:
+		data[4] = 0x00;
+		break;
+	case CXD2880_DTV_BW_7_MHZ:
+		data[4] = 0x01;
+		break;
+	case CXD2880_DTV_BW_8_MHZ:
+		data[4] = 0x02;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	data[5] = 0x00;
+
+	freq_khz += shift_frequency_khz;
+
+	data[6] = (freq_khz >> 16) & 0x0f;
+	data[7] = (freq_khz >> 8) & 0xff;
+	data[8] = freq_khz & 0xff;
+	data[9] = 0xff;
+	data[10] = 0xfe;
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_SYS,
+				       0x52, data, 11);
+}
+
+static int x_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+		   enum cxd2880_dtv_bandwidth bandwidth,
+		   enum cxd2880_tnrdmd_clockmode clk_mode,
+		   int shift_frequency_khz)
+{
+	u8 data[3] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x11);
+	if (ret)
+		return ret;
+
+	data[0] = 0x01;
+	data[1] = 0x0e;
+	data[2] = 0x01;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x2d, data, 3);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  x_tune2_seq1,
+					  ARRAY_SIZE(x_tune2_seq1));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x2c, data, 1);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x60, data[0]);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  x_tune2_seq2,
+					  ARRAY_SIZE(x_tune2_seq2));
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  x_tune2_seq3,
+					  ARRAY_SIZE(x_tune2_seq3));
+	if (ret)
+		return ret;
+
+	if (shift_frequency_khz != 0) {
+		int shift_freq = 0;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0xe1);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x60, data, 2);
+		if (ret)
+			return ret;
+
+		shift_freq = shift_frequency_khz * 1000;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+		default:
+			if (shift_freq >= 0)
+				shift_freq = (shift_freq + 183 / 2) / 183;
+			else
+				shift_freq = (shift_freq - 183 / 2) / 183;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			if (shift_freq >= 0)
+				shift_freq = (shift_freq + 178 / 2) / 178;
+			else
+				shift_freq = (shift_freq - 178 / 2) / 178;
+			break;
+		}
+
+		shift_freq +=
+		    cxd2880_convert2s_complement((data[0] << 8) | data[1], 16);
+
+		if (shift_freq > 32767)
+			shift_freq = 32767;
+		else if (shift_freq < -32768)
+			shift_freq = -32768;
+
+		data[0] = (shift_freq >> 8) & 0xff;
+		data[1] = shift_freq & 0xff;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x60, data, 2);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x69, data, 1);
+		if (ret)
+			return ret;
+
+		shift_freq = -shift_frequency_khz;
+
+		if (bandwidth == CXD2880_DTV_BW_1_7_MHZ) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+			default:
+				if (shift_freq >= 0)
+					shift_freq =
+					    (shift_freq * 1000 +
+					     17578 / 2) / 17578;
+				else
+					shift_freq =
+					    (shift_freq * 1000 -
+					     17578 / 2) / 17578;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				if (shift_freq >= 0)
+					shift_freq =
+					    (shift_freq * 1000 +
+					     17090 / 2) / 17090;
+				else
+					shift_freq =
+					    (shift_freq * 1000 -
+					     17090 / 2) / 17090;
+				break;
+			}
+		} else {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+			default:
+				if (shift_freq >= 0)
+					shift_freq =
+					    (shift_freq * 1000 +
+					     35156 / 2) / 35156;
+				else
+					shift_freq =
+					    (shift_freq * 1000 -
+					     35156 / 2) / 35156;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				if (shift_freq >= 0)
+					shift_freq =
+					    (shift_freq * 1000 +
+					     34180 / 2) / 34180;
+				else
+					shift_freq =
+					    (shift_freq * 1000 -
+					     34180 / 2) / 34180;
+				break;
+			}
+		}
+
+		shift_freq += cxd2880_convert2s_complement(data[0], 8);
+
+		if (shift_freq > 127)
+			shift_freq = 127;
+		else if (shift_freq < -128)
+			shift_freq = -128;
+
+		data[0] = shift_freq & 0xff;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x69, data[0]);
+		if (ret)
+			return ret;
+	}
+
+	if (tnr_dmd->create_param.stationary_use) {
+		ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+						  CXD2880_IO_TGT_DMD,
+						  x_tune2_seq4,
+						  ARRAY_SIZE(x_tune2_seq4));
+		if (ret)
+			return ret;
+	}
+
+	return cxd2880_io_write_multi_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD,
+					   x_tune2_seq5,
+					   ARRAY_SIZE(x_tune2_seq5));
+}
+
+static int x_tune3(struct cxd2880_tnrdmd *tnr_dmd,
+		   enum cxd2880_dtv_sys sys,
+		   u8 en_fef_intmtnt_ctrl)
+{
+	u8 data[6] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  x_tune3_seq,
+					  ARRAY_SIZE(x_tune3_seq));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl)
+		memset(data, 0x01, sizeof(data));
+	else
+		memset(data, 0x00, sizeof(data));
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0xef, data, 6);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x2d);
+	if (ret)
+		return ret;
+	if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl)
+		data[0] = 0x00;
+	else
+		data[0] = 0x01;
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xb1, data[0]);
+}
+
+static int x_tune4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[2] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+						CXD2880_IO_TGT_SYS,
+						0x00, 0x00);
+	if (ret)
+		return ret;
+	data[0] = 0x14;
+	data[1] = 0x00;
+	ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+						CXD2880_IO_TGT_SYS,
+						0x55, data, 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+	data[0] = 0x0b;
+	data[1] = 0xff;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x53, data, 2);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x57, 0x01);
+	if (ret)
+		return ret;
+	data[0] = 0x0b;
+	data[1] = 0xff;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x55, data, 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+						CXD2880_IO_TGT_SYS,
+						0x00, 0x00);
+	if (ret)
+		return ret;
+	data[0] = 0x14;
+	data[1] = 0x00;
+	ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+						 CXD2880_IO_TGT_SYS,
+						 0x53, data, 2);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+						CXD2880_IO_TGT_SYS,
+						0x57, 0x02);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  x_tune4_seq,
+					  ARRAY_SIZE(x_tune4_seq));
+	if (ret)
+		return ret;
+
+	return cxd2880_io_write_multi_regs(tnr_dmd->diver_sub->io,
+					   CXD2880_IO_TGT_DMD,
+					   x_tune4_seq,
+					   ARRAY_SIZE(x_tune4_seq));
+}
+
+static int x_sleep1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[3] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  x_sleep1_seq,
+					  ARRAY_SIZE(x_sleep1_seq));
+	if (ret)
+		return ret;
+
+	data[0] = 0x00;
+	data[1] = 0x00;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x53, data, 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+						CXD2880_IO_TGT_SYS,
+						0x00, 0x00);
+	if (ret)
+		return ret;
+	data[0] = 0x1f;
+	data[1] = 0xff;
+	data[2] = 0x03;
+	ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+						 CXD2880_IO_TGT_SYS,
+						 0x55, data, 3);
+	if (ret)
+		return ret;
+	data[0] = 0x00;
+	data[1] = 0x00;
+	ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+						 CXD2880_IO_TGT_SYS,
+						 0x53, data, 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+	data[0] = 0x1f;
+	data[1] = 0xff;
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_SYS,
+				       0x55, data, 2);
+}
+
+static int x_sleep2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data = 0;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  x_sleep2_seq1,
+					  ARRAY_SIZE(x_sleep2_seq1));
+	if (ret)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xb2, &data, 1);
+	if (ret)
+		return ret;
+
+	if ((data & 0x01) == 0x00)
+		return -EINVAL;
+
+	return cxd2880_io_write_multi_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS,
+					   x_sleep2_seq2,
+					   ARRAY_SIZE(x_sleep2_seq2));
+}
+
+static int x_sleep3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	return cxd2880_io_write_multi_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD,
+					   x_sleep3_seq,
+					   ARRAY_SIZE(x_sleep3_seq));
+}
+
+static int x_sleep4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	return cxd2880_io_write_multi_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD,
+					   x_sleep4_seq,
+					   ARRAY_SIZE(x_sleep4_seq));
+}
+
+static int spll_reset(struct cxd2880_tnrdmd *tnr_dmd,
+		      enum cxd2880_tnrdmd_clockmode clockmode)
+{
+	u8 data[4] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  spll_reset_seq1,
+					  ARRAY_SIZE(spll_reset_seq1));
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  spll_reset_seq2,
+					  ARRAY_SIZE(spll_reset_seq2));
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  spll_reset_seq3,
+					  ARRAY_SIZE(spll_reset_seq3));
+	if (ret)
+		return ret;
+
+	switch (clockmode) {
+	case CXD2880_TNRDMD_CLOCKMODE_A:
+		data[0] = 0x00;
+		break;
+
+	case CXD2880_TNRDMD_CLOCKMODE_B:
+		data[0] = 0x01;
+		break;
+
+	case CXD2880_TNRDMD_CLOCKMODE_C:
+		data[0] = 0x02;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x30, data[0]);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x22, 0x00);
+	if (ret)
+		return ret;
+
+	usleep_range(2000, 3000);
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x0a);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x10, data, 1);
+	if (ret)
+		return ret;
+	if ((data[0] & 0x01) == 0x00)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  spll_reset_seq4,
+					  ARRAY_SIZE(spll_reset_seq4));
+	if (ret)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  spll_reset_seq5,
+					  ARRAY_SIZE(spll_reset_seq5));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	memset(data, 0x00, sizeof(data));
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_SYS,
+				       0x26, data, 4);
+}
+
+static int t_power_x(struct cxd2880_tnrdmd *tnr_dmd, u8 on)
+{
+	u8 data[3] = { 0 };
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  t_power_x_seq1,
+					  ARRAY_SIZE(t_power_x_seq1));
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  t_power_x_seq2,
+					  ARRAY_SIZE(t_power_x_seq2));
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  t_power_x_seq3,
+					  ARRAY_SIZE(t_power_x_seq3));
+	if (ret)
+		return ret;
+
+	if (on) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS,
+					     0x2b, 0x01);
+		if (ret)
+			return ret;
+
+		usleep_range(1000, 2000);
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS,
+					     0x00, 0x0a);
+		if (ret)
+			return ret;
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS,
+					     0x12, data, 1);
+		if (ret)
+			return ret;
+		if ((data[0] & 0x01) == 0)
+			return -EINVAL;
+
+		ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+						  CXD2880_IO_TGT_SYS,
+						  t_power_x_seq4,
+						  ARRAY_SIZE(t_power_x_seq4));
+		if (ret)
+			return ret;
+	} else {
+		data[0] = 0x03;
+		data[1] = 0x00;
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_SYS,
+					      0x2a, data, 2);
+		if (ret)
+			return ret;
+
+		usleep_range(1000, 2000);
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS,
+					     0x00, 0x0a);
+		if (ret)
+			return ret;
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS,
+					     0x13, data, 1);
+		if (ret)
+			return ret;
+		if ((data[0] & 0x01) == 0)
+			return -EINVAL;
+	}
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  t_power_x_seq5,
+					  ARRAY_SIZE(t_power_x_seq5));
+	if (ret)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x0a);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x11, data, 1);
+	if (ret)
+		return ret;
+	if ((data[0] & 0x01) == 0)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  t_power_x_seq6,
+					  ARRAY_SIZE(t_power_x_seq6));
+	if (ret)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  t_power_x_seq7,
+					  ARRAY_SIZE(t_power_x_seq7));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	memset(data, 0x00, sizeof(data));
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_SYS,
+				       0x27, data, 3);
+}
+
+struct cxd2880_tnrdmd_ts_clk_cfg {
+	u8 srl_clk_mode;
+	u8 srl_duty_mode;
+	u8 ts_clk_period;
+};
+
+static int set_ts_clk_mode_and_freq(struct cxd2880_tnrdmd *tnr_dmd,
+				    enum cxd2880_dtv_sys sys)
+{
+	int ret;
+	u8 backwards_compatible = 0;
+	struct cxd2880_tnrdmd_ts_clk_cfg ts_clk_cfg;
+	u8 ts_rate_ctrl_off = 0;
+	u8 ts_in_off = 0;
+	u8 ts_clk_manaul_on = 0;
+	u8 data = 0;
+
+	static const struct cxd2880_tnrdmd_ts_clk_cfg srl_ts_clk_stgs[2][2] = {
+		{
+			{3, 1, 8,},
+			{0, 2, 16,}
+		}, {
+			{1, 1, 8,},
+			{2, 2, 16,}
+		}
+	};
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->is_ts_backwards_compatible_mode) {
+		backwards_compatible = 1;
+		ts_rate_ctrl_off = 1;
+		ts_in_off = 1;
+	} else {
+		backwards_compatible = 0;
+		ts_rate_ctrl_off = 0;
+		ts_in_off = 0;
+	}
+
+	if (tnr_dmd->ts_byte_clk_manual_setting) {
+		ts_clk_manaul_on = 1;
+		ts_rate_ctrl_off = 0;
+	}
+
+	ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xd3, ts_rate_ctrl_off, 0x01);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xde, ts_in_off, 0x01);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xda, ts_clk_manaul_on, 0x01);
+	if (ret)
+		return ret;
+
+	ts_clk_cfg = srl_ts_clk_stgs[tnr_dmd->srl_ts_clk_mod_cnts]
+				    [tnr_dmd->srl_ts_clk_frq];
+
+	if (tnr_dmd->ts_byte_clk_manual_setting)
+		ts_clk_cfg.ts_clk_period = tnr_dmd->ts_byte_clk_manual_setting;
+
+	ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xc4, ts_clk_cfg.srl_clk_mode, 0x03);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xd1, ts_clk_cfg.srl_duty_mode, 0x03);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD, 0xd9,
+				     ts_clk_cfg.ts_clk_period);
+	if (ret)
+		return ret;
+
+	data = backwards_compatible ? 0x00 : 0x01;
+
+	if (sys == CXD2880_DTV_SYS_DVBT) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x10);
+		if (ret)
+			return ret;
+
+		ret =
+		    cxd2880_io_set_reg_bits(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD,
+					    0x66, data, 0x01);
+	}
+
+	return ret;
+}
+
+static int pid_ftr_setting(struct cxd2880_tnrdmd *tnr_dmd,
+			   struct cxd2880_tnrdmd_pid_ftr_cfg
+			   *pid_ftr_cfg)
+{
+	int i;
+	int ret;
+	u8 data[65];
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	if (!pid_ftr_cfg)
+		return tnr_dmd->io->write_reg(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x50, 0x02);
+
+	data[0] = pid_ftr_cfg->is_negative ? 0x01 : 0x00;
+
+	for (i = 0; i < 32; i++) {
+		if (pid_ftr_cfg->pid_cfg[i].is_en) {
+			data[1 + (i * 2)] = (pid_ftr_cfg->pid_cfg[i].pid >> 8) | 0x20;
+			data[2 + (i * 2)] =  pid_ftr_cfg->pid_cfg[i].pid & 0xff;
+		} else {
+			data[1 + (i * 2)] = 0x00;
+			data[2 + (i * 2)] = 0x00;
+		}
+	}
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_DMD,
+				       0x50, data, 65);
+}
+
+static int load_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	int ret;
+	u8 i;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     tnr_dmd->cfg_mem[i].tgt,
+					     0x00, tnr_dmd->cfg_mem[i].bank);
+		if (ret)
+			return ret;
+
+		ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+					      tnr_dmd->cfg_mem[i].tgt,
+					      tnr_dmd->cfg_mem[i].address,
+					      tnr_dmd->cfg_mem[i].value,
+					      tnr_dmd->cfg_mem[i].bit_mask);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int set_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd,
+		       enum cxd2880_io_tgt tgt,
+		       u8 bank, u8 address, u8 value, u8 bit_mask)
+{
+	u8 i;
+	u8 value_stored = 0;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+		if (value_stored == 0 &&
+		    tnr_dmd->cfg_mem[i].tgt == tgt &&
+		    tnr_dmd->cfg_mem[i].bank == bank &&
+		    tnr_dmd->cfg_mem[i].address == address) {
+			tnr_dmd->cfg_mem[i].value &= ~bit_mask;
+			tnr_dmd->cfg_mem[i].value |= (value & bit_mask);
+
+			tnr_dmd->cfg_mem[i].bit_mask |= bit_mask;
+
+			value_stored = 1;
+		}
+	}
+
+	if (value_stored)
+		return 0;
+
+	if (tnr_dmd->cfg_mem_last_entry < CXD2880_TNRDMD_MAX_CFG_MEM_COUNT) {
+		tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].tgt = tgt;
+		tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bank = bank;
+		tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].address = address;
+		tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].value = (value & bit_mask);
+		tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bit_mask = bit_mask;
+		tnr_dmd->cfg_mem_last_entry++;
+	} else {
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
+			  struct cxd2880_io *io,
+			  struct cxd2880_tnrdmd_create_param
+			  *create_param)
+{
+	if (!tnr_dmd || !io || !create_param)
+		return -EINVAL;
+
+	memset(tnr_dmd, 0, sizeof(struct cxd2880_tnrdmd));
+
+	tnr_dmd->io = io;
+	tnr_dmd->create_param = *create_param;
+
+	tnr_dmd->diver_mode = CXD2880_TNRDMD_DIVERMODE_SINGLE;
+	tnr_dmd->diver_sub = NULL;
+
+	tnr_dmd->srl_ts_clk_mod_cnts = 1;
+	tnr_dmd->en_fef_intmtnt_base = 1;
+	tnr_dmd->en_fef_intmtnt_lite = 1;
+	tnr_dmd->rf_lvl_cmpstn = NULL;
+	tnr_dmd->lna_thrs_tbl_air = NULL;
+	tnr_dmd->lna_thrs_tbl_cable = NULL;
+	atomic_set(&tnr_dmd->cancel, 0);
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
+				*tnr_dmd_main,
+				struct cxd2880_io *io_main,
+				struct cxd2880_tnrdmd *tnr_dmd_sub,
+				struct cxd2880_io *io_sub,
+				struct
+				cxd2880_tnrdmd_diver_create_param
+				*create_param)
+{
+	struct cxd2880_tnrdmd_create_param *main_param, *sub_param;
+
+	if (!tnr_dmd_main || !io_main || !tnr_dmd_sub || !io_sub ||
+	    !create_param)
+		return -EINVAL;
+
+	memset(tnr_dmd_main, 0, sizeof(struct cxd2880_tnrdmd));
+	memset(tnr_dmd_sub, 0, sizeof(struct cxd2880_tnrdmd));
+
+	main_param = &tnr_dmd_main->create_param;
+	sub_param = &tnr_dmd_sub->create_param;
+
+	tnr_dmd_main->io = io_main;
+	tnr_dmd_main->diver_mode = CXD2880_TNRDMD_DIVERMODE_MAIN;
+	tnr_dmd_main->diver_sub = tnr_dmd_sub;
+	tnr_dmd_main->create_param.en_internal_ldo =
+	    create_param->en_internal_ldo;
+
+	main_param->ts_output_if = create_param->ts_output_if;
+	main_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_MASTER;
+	main_param->xosc_cap = create_param->xosc_cap_main;
+	main_param->xosc_i = create_param->xosc_i_main;
+	main_param->is_cxd2881gg = create_param->is_cxd2881gg;
+	main_param->stationary_use = create_param->stationary_use;
+
+	tnr_dmd_sub->io = io_sub;
+	tnr_dmd_sub->diver_mode = CXD2880_TNRDMD_DIVERMODE_SUB;
+	tnr_dmd_sub->diver_sub = NULL;
+
+	sub_param->en_internal_ldo = create_param->en_internal_ldo;
+	sub_param->ts_output_if = create_param->ts_output_if;
+	sub_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_SLAVE;
+	sub_param->xosc_cap = 0;
+	sub_param->xosc_i = create_param->xosc_i_sub;
+	sub_param->is_cxd2881gg = create_param->is_cxd2881gg;
+	sub_param->stationary_use = create_param->stationary_use;
+
+	tnr_dmd_main->srl_ts_clk_mod_cnts = 1;
+	tnr_dmd_main->en_fef_intmtnt_base = 1;
+	tnr_dmd_main->en_fef_intmtnt_lite = 1;
+	tnr_dmd_main->rf_lvl_cmpstn = NULL;
+	tnr_dmd_main->lna_thrs_tbl_air = NULL;
+	tnr_dmd_main->lna_thrs_tbl_cable = NULL;
+
+	tnr_dmd_sub->srl_ts_clk_mod_cnts = 1;
+	tnr_dmd_sub->en_fef_intmtnt_base = 1;
+	tnr_dmd_sub->en_fef_intmtnt_lite = 1;
+	tnr_dmd_sub->rf_lvl_cmpstn = NULL;
+	tnr_dmd_sub->lna_thrs_tbl_air = NULL;
+	tnr_dmd_sub->lna_thrs_tbl_cable = NULL;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	int ret;
+
+	if (!tnr_dmd || tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	tnr_dmd->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+	tnr_dmd->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+	tnr_dmd->frequency_khz = 0;
+	tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+	tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+	tnr_dmd->scan_mode = 0;
+	atomic_set(&tnr_dmd->cancel, 0);
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		tnr_dmd->diver_sub->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+		tnr_dmd->diver_sub->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+		tnr_dmd->diver_sub->frequency_khz = 0;
+		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+		tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+		tnr_dmd->diver_sub->scan_mode = 0;
+		atomic_set(&tnr_dmd->diver_sub->cancel, 0);
+	}
+
+	ret = cxd2880_tnrdmd_chip_id(tnr_dmd, &tnr_dmd->chip_id);
+	if (ret)
+		return ret;
+
+	if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->chip_id))
+		return -ENOTTY;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    cxd2880_tnrdmd_chip_id(tnr_dmd->diver_sub,
+					   &tnr_dmd->diver_sub->chip_id);
+		if (ret)
+			return ret;
+
+		if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->diver_sub->chip_id))
+			return -ENOTTY;
+	}
+
+	ret = p_init1(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = p_init1(tnr_dmd->diver_sub);
+		if (ret)
+			return ret;
+	}
+
+	usleep_range(1000, 2000);
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = p_init2(tnr_dmd->diver_sub);
+		if (ret)
+			return ret;
+	}
+
+	ret = p_init2(tnr_dmd);
+	if (ret)
+		return ret;
+
+	usleep_range(5000, 6000);
+
+	ret = p_init3(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = p_init3(tnr_dmd->diver_sub);
+		if (ret)
+			return ret;
+	}
+
+	ret = rf_init1(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+		ret = rf_init1(tnr_dmd->diver_sub);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 cpu_task_completed;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+						     &cpu_task_completed);
+	if (ret)
+		return ret;
+
+	if (!cpu_task_completed)
+		return -EINVAL;
+
+	ret = rf_init2(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = rf_init2(tnr_dmd->diver_sub);
+		if (ret)
+			return ret;
+	}
+
+	ret = load_cfg_mem(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = load_cfg_mem(tnr_dmd->diver_sub);
+		if (ret)
+			return ret;
+	}
+
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
+					     *tnr_dmd,
+					     u8 *task_completed)
+{
+	u16 cpu_status = 0;
+	int ret;
+
+	if (!tnr_dmd || !task_completed)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd, &cpu_status);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (cpu_status == 0)
+			*task_completed = 1;
+		else
+			*task_completed = 0;
+
+		return ret;
+	}
+	if (cpu_status != 0) {
+		*task_completed = 0;
+		return ret;
+	}
+
+	ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status);
+	if (ret)
+		return ret;
+
+	if (cpu_status == 0)
+		*task_completed = 1;
+	else
+		*task_completed = 0;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd *tnr_dmd,
+					enum cxd2880_dtv_sys sys,
+					u32 frequency_khz,
+					enum cxd2880_dtv_bandwidth
+					bandwidth, u8 one_seg_opt,
+					u8 one_seg_opt_shft_dir)
+{
+	u8 data;
+	enum cxd2880_tnrdmd_clockmode new_clk_mode =
+				CXD2880_TNRDMD_CLOCKMODE_A;
+	int shift_frequency_khz;
+	u8 cpu_task_completed;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (frequency_khz < 4000)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_sleep(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00,
+				     0x00);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x2b,
+				     &data,
+				     1);
+	if (ret)
+		return ret;
+
+	switch (sys) {
+	case CXD2880_DTV_SYS_DVBT:
+		if (data == 0x00) {
+			ret = t_power_x(tnr_dmd, 1);
+			if (ret)
+				return ret;
+
+			if (tnr_dmd->diver_mode ==
+			    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+				ret = t_power_x(tnr_dmd->diver_sub, 1);
+				if (ret)
+					return ret;
+			}
+		}
+		break;
+
+	case CXD2880_DTV_SYS_DVBT2:
+		if (data == 0x01) {
+			ret = t_power_x(tnr_dmd, 0);
+			if (ret)
+				return ret;
+
+			if (tnr_dmd->diver_mode ==
+			    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+				ret = t_power_x(tnr_dmd->diver_sub, 0);
+				if (ret)
+					return ret;
+			}
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = spll_reset(tnr_dmd, new_clk_mode);
+	if (ret)
+		return ret;
+
+	tnr_dmd->clk_mode = new_clk_mode;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = spll_reset(tnr_dmd->diver_sub, new_clk_mode);
+		if (ret)
+			return ret;
+
+		tnr_dmd->diver_sub->clk_mode = new_clk_mode;
+	}
+
+	ret = load_cfg_mem(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = load_cfg_mem(tnr_dmd->diver_sub);
+		if (ret)
+			return ret;
+	}
+
+	if (one_seg_opt) {
+		if (tnr_dmd->diver_mode ==
+		    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			shift_frequency_khz = 350;
+		} else {
+			if (one_seg_opt_shft_dir)
+				shift_frequency_khz = 350;
+			else
+				shift_frequency_khz = -350;
+
+			if (tnr_dmd->create_param.xtal_share_type ==
+			    CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
+				shift_frequency_khz *= -1;
+		}
+	} else {
+		if (tnr_dmd->diver_mode ==
+		    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			shift_frequency_khz = 150;
+		} else {
+			switch (tnr_dmd->create_param.xtal_share_type) {
+			case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+			case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+			default:
+				shift_frequency_khz = 0;
+				break;
+			case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+				shift_frequency_khz = 150;
+				break;
+			case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+				shift_frequency_khz = -150;
+				break;
+			}
+		}
+	}
+
+	ret =
+	    x_tune1(tnr_dmd, sys, frequency_khz, bandwidth,
+		    tnr_dmd->is_cable_input, shift_frequency_khz);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    x_tune1(tnr_dmd->diver_sub, sys, frequency_khz,
+			    bandwidth, tnr_dmd->is_cable_input,
+			    -shift_frequency_khz);
+		if (ret)
+			return ret;
+	}
+
+	usleep_range(10000, 11000);
+
+	ret =
+	    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+					     &cpu_task_completed);
+	if (ret)
+		return ret;
+
+	if (!cpu_task_completed)
+		return -EINVAL;
+
+	ret =
+	    x_tune2(tnr_dmd, bandwidth, tnr_dmd->clk_mode,
+		    shift_frequency_khz);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    x_tune2(tnr_dmd->diver_sub, bandwidth,
+			    tnr_dmd->diver_sub->clk_mode,
+			    -shift_frequency_khz);
+		if (ret)
+			return ret;
+	}
+
+	if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) {
+		ret = set_ts_clk_mode_and_freq(tnr_dmd, sys);
+	} else {
+		struct cxd2880_tnrdmd_pid_ftr_cfg *pid_ftr_cfg;
+
+		if (tnr_dmd->pid_ftr_cfg_en)
+			pid_ftr_cfg = &tnr_dmd->pid_ftr_cfg;
+		else
+			pid_ftr_cfg = NULL;
+
+		ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+	}
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
+					*tnr_dmd,
+					enum cxd2880_dtv_sys sys,
+					u8 en_fef_intmtnt_ctrl)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = x_tune3(tnr_dmd, sys, en_fef_intmtnt_ctrl);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_tune3(tnr_dmd->diver_sub, sys, en_fef_intmtnt_ctrl);
+		if (ret)
+			return ret;
+		ret = x_tune4(tnr_dmd);
+		if (ret)
+			return ret;
+	}
+
+	return cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1);
+}
+
+int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state == CXD2880_TNRDMD_STATE_SLEEP)
+		return 0;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 0);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_sleep1(tnr_dmd);
+		if (ret)
+			return ret;
+	}
+
+	ret = x_sleep2(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_sleep2(tnr_dmd->diver_sub);
+		if (ret)
+			return ret;
+	}
+
+	switch (tnr_dmd->sys) {
+	case CXD2880_DTV_SYS_DVBT:
+		ret = cxd2880_tnrdmd_dvbt_sleep_setting(tnr_dmd);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_DTV_SYS_DVBT2:
+		ret = cxd2880_tnrdmd_dvbt2_sleep_setting(tnr_dmd);
+		if (ret)
+			return ret;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = x_sleep3(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_sleep3(tnr_dmd->diver_sub);
+		if (ret)
+			return ret;
+	}
+
+	ret = x_sleep4(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_sleep4(tnr_dmd->diver_sub);
+		if (ret)
+			return ret;
+	}
+
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+	tnr_dmd->frequency_khz = 0;
+	tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+	tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+		tnr_dmd->diver_sub->frequency_khz = 0;
+		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+		tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+			   enum cxd2880_tnrdmd_cfg_id id,
+			   int value)
+{
+	int ret = 0;
+	u8 data[2] = { 0 };
+	u8 need_sub_setting = 0;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	switch (id) {
+	case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xc4,
+							 value ? 0x00 : 0x10,
+							 0x10);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xc5,
+							 value ? 0x00 : 0x02,
+							 0x02);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xc5,
+							 value ? 0x00 : 0x04,
+							 0x04);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xcb,
+							 value ? 0x00 : 0x01,
+							 0x01);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xc5,
+							 value ? 0x01 : 0x00,
+							 0x01);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSCLK_CONT:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		tnr_dmd->srl_ts_clk_mod_cnts = value ? 0x01 : 0x00;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSCLK_MASK:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		if (value < 0 || value > 0x1f)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xc6, value,
+							 0x1f);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSVALID_MASK:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		if (value < 0 || value > 0x1f)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xc8, value,
+							 0x1f);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSERR_MASK:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		if (value < 0 || value > 0x1f)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xc9, value,
+							 0x1f);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSERR_VALID_DIS:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x91,
+							 value ? 0x01 : 0x00,
+							 0x01);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSPIN_CURRENT:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x51, value,
+							 0x3f);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x50,
+							 value ? 0x80 : 0x00,
+							 0x80);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSPIN_PULLUP:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x50, value,
+							 0x3f);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSCLK_FREQ:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		if (value < 0 || value > 1)
+			return -EINVAL;
+
+		tnr_dmd->srl_ts_clk_frq =
+		    (enum cxd2880_tnrdmd_serial_ts_clk)value;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		if (value < 0 || value > 0xff)
+			return -EINVAL;
+
+		tnr_dmd->ts_byte_clk_manual_setting = value;
+
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_PACKET_GAP:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		if (value < 0 || value > 7)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xd6, value,
+							 0x07);
+		if (ret)
+			return ret;
+
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EINVAL;
+
+		tnr_dmd->is_ts_backwards_compatible_mode = value ? 1 : 0;
+
+		break;
+
+	case CXD2880_TNRDMD_CFG_PWM_VALUE:
+		if (value < 0 || value > 0x1000)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x22,
+							 value ? 0x01 : 0x00,
+							 0x01);
+		if (ret)
+			return ret;
+
+		data[0] = (value >> 8) & 0x1f;
+		data[1] = value & 0xff;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x23,
+							 data[0], 0x1f);
+		if (ret)
+			return ret;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x24,
+							 data[1], 0xff);
+		if (ret)
+			return ret;
+
+		break;
+
+	case CXD2880_TNRDMD_CFG_INTERRUPT:
+		data[0] = (value >> 8) & 0xff;
+		data[1] = value & 0xff;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x48, data[0],
+							 0xff);
+		if (ret)
+			return ret;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x49, data[1],
+							 0xff);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL:
+		data[0] = value & 0x07;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x4a, data[0],
+							 0x07);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL:
+		data[0] = (value & 0x07) << 3;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x4a, data[0],
+							 0x38);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE:
+		if (value < CXD2880_TNRDMD_CLOCKMODE_UNKNOWN ||
+		    value > CXD2880_TNRDMD_CLOCKMODE_C)
+			return -EINVAL;
+		tnr_dmd->fixed_clk_mode = (enum cxd2880_tnrdmd_clockmode)value;
+		break;
+
+	case CXD2880_TNRDMD_CFG_CABLE_INPUT:
+		tnr_dmd->is_cable_input = value ? 1 : 0;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE:
+		tnr_dmd->en_fef_intmtnt_base = value ? 1 : 0;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE:
+		tnr_dmd->en_fef_intmtnt_lite = value ? 1 : 0;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS:
+		data[0] = (value >> 8) & 0x07;
+		data[1] = value & 0xff;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x99, data[0],
+							 0x07);
+		if (ret)
+			return ret;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9a, data[1],
+							 0xff);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS:
+		data[0] = (value >> 8) & 0x07;
+		data[1] = value & 0xff;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9b, data[0],
+							 0x07);
+		if (ret)
+			return ret;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9c, data[1],
+							 0xff);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS:
+		data[0] = (value >> 8) & 0x07;
+		data[1] = value & 0xff;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9d, data[0],
+							 0x07);
+		if (ret)
+			return ret;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9e, data[1],
+							 0xff);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST:
+		tnr_dmd->blind_tune_dvbt2_first = value ? 1 : 0;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD:
+		if (value < 0 || value > 31)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x10, 0x60,
+							 value & 0x1f, 0x1f);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD:
+		if (value < 0 || value > 7)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x10, 0x6f,
+							 value & 0x07, 0x07);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_BBER_MES:
+		if (value < 0 || value > 15)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x20, 0x72,
+							 value & 0x0f, 0x0f);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_LBER_MES:
+		if (value < 0 || value > 15)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x20, 0x6f,
+							 value & 0x0f, 0x0f);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT_PER_MES:
+		if (value < 0 || value > 15)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x10, 0x5c,
+							 value & 0x0f, 0x0f);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_PER_MES:
+		if (value < 0 || value > 15)
+			return -EINVAL;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x24, 0xdc,
+							 value & 0x0f, 0x0f);
+		if (ret)
+			return ret;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (need_sub_setting &&
+	    tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+		ret = cxd2880_tnrdmd_set_cfg(tnr_dmd->diver_sub, id, value);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+				u8 id,
+				u8 en,
+				enum cxd2880_tnrdmd_gpio_mode mode,
+				u8 open_drain, u8 invert)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (id > 2)
+		return -EINVAL;
+
+	if (mode > CXD2880_TNRDMD_GPIO_MODE_EEW)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+						 0x00, 0x40 + id, mode,
+						 0x0f);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+						 0x00, 0x43,
+						 open_drain ? (1 << id) : 0,
+						 1 << id);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+						 0x00, 0x44,
+						 invert ? (1 << id) : 0,
+						 1 << id);
+	if (ret)
+		return ret;
+
+	return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+						    CXD2880_IO_TGT_SYS,
+						    0x00, 0x45,
+						    en ? 0 : (1 << id),
+						    1 << id);
+}
+
+int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				    u8 id,
+				    u8 en,
+				    enum cxd2880_tnrdmd_gpio_mode
+				    mode, u8 open_drain, u8 invert)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode,
+					   open_drain, invert);
+}
+
+int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
+			     u8 id, u8 *value)
+{
+	u8 data = 0;
+	int ret;
+
+	if (!tnr_dmd || !value)
+		return -EINVAL;
+
+	if (id > 2)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x0a);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x20, &data, 1);
+	if (ret)
+		return ret;
+
+	*value = (data >> id) & 0x01;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				 u8 id, u8 *value)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value);
+}
+
+int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+			      u8 id, u8 value)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (id > 2)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+						    CXD2880_IO_TGT_SYS,
+						    0x00, 0x46,
+						    value ? (1 << id) : 0,
+						    1 << id);
+}
+
+int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				  u8 id, u8 value)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value);
+}
+
+int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
+				  u16 *value)
+{
+	int ret;
+	u8 data[2] = { 0 };
+
+	if (!tnr_dmd || !value)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x0a);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x15, data, 2);
+	if (ret)
+		return ret;
+
+	*value = (data[0] << 8) | data[1];
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
+				   u16 value)
+{
+	int ret;
+	u8 data[2] = { 0 };
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	data[0] = (value >> 8) & 0xff;
+	data[1] = value & 0xff;
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_SYS,
+				       0x3c, data, 2);
+}
+
+int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
+				u8 clear_overflow_flag,
+				u8 clear_underflow_flag,
+				u8 clear_buf)
+{
+	int ret;
+	u8 data[2] = { 0 };
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	data[0] = clear_overflow_flag ? 0x02 : 0x00;
+	data[0] |= clear_underflow_flag ? 0x01 : 0x00;
+	data[1] = clear_buf ? 0x01 : 0x00;
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_DMD,
+				       0x9f, data, 2);
+}
+
+int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
+			   enum cxd2880_tnrdmd_chip_id *chip_id)
+{
+	int ret;
+	u8 data = 0;
+
+	if (!tnr_dmd || !chip_id)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0xfd, &data, 1);
+	if (ret)
+		return ret;
+
+	*chip_id = (enum cxd2880_tnrdmd_chip_id)data;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
+					 *tnr_dmd,
+					 enum cxd2880_io_tgt tgt,
+					 u8 bank, u8 address,
+					 u8 value, u8 bit_mask)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io, tgt, 0x00, bank);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+				      tgt, address, value, bit_mask);
+	if (ret)
+		return ret;
+
+	return set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask);
+}
+
+int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
+				 enum cxd2880_dtv_sys sys,
+				 u8 scan_mode_end)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	tnr_dmd->scan_mode = scan_mode_end;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys,
+						    scan_mode_end);
+	else
+		return 0;
+}
+
+int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_tnrdmd_pid_ftr_cfg
+			       *pid_ftr_cfg)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS)
+		return -ENOTTY;
+
+	if (pid_ftr_cfg) {
+		tnr_dmd->pid_ftr_cfg = *pid_ftr_cfg;
+		tnr_dmd->pid_ftr_cfg_en = 1;
+	} else {
+		tnr_dmd->pid_ftr_cfg_en = 0;
+	}
+
+	if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE)
+		return pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+	else
+		return 0;
+}
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     int (*rf_lvl_cmpstn)
+				     (struct cxd2880_tnrdmd *,
+				     int *))
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	tnr_dmd->rf_lvl_cmpstn = rf_lvl_cmpstn;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
+					 *tnr_dmd,
+					 int (*rf_lvl_cmpstn)
+					 (struct cxd2880_tnrdmd *,
+					 int *))
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub,
+						rf_lvl_cmpstn);
+}
+
+int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
+				struct cxd2880_tnrdmd_lna_thrs_tbl_air
+				*tbl_air,
+				struct cxd2880_tnrdmd_lna_thrs_tbl_cable
+				*tbl_cable)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	tnr_dmd->lna_thrs_tbl_air = tbl_air;
+	tnr_dmd->lna_thrs_tbl_cable = tbl_cable;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				    struct
+				    cxd2880_tnrdmd_lna_thrs_tbl_air
+				    *tbl_air,
+				    struct cxd2880_tnrdmd_lna_thrs_tbl_cable
+				    *tbl_cable)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub,
+					   tbl_air, tbl_cable);
+}
+
+int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
+				       *tnr_dmd, u8 en, u8 value)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+		return -EINVAL;
+
+	if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS)
+		return -ENOTTY;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	if (en) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS,
+					     0x50, ((value & 0x1f) | 0x80));
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS,
+					     0x52, (value & 0x1f));
+	} else {
+		ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+						  CXD2880_IO_TGT_SYS,
+						  set_ts_pin_seq,
+						  ARRAY_SIZE(set_ts_pin_seq));
+		if (ret)
+			return ret;
+
+		ret = load_cfg_mem(tnr_dmd);
+	}
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
+				 u8 en)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	switch (tnr_dmd->create_param.ts_output_if) {
+	case CXD2880_TNRDMD_TSOUT_IF_TS:
+		if (en) {
+			ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+							  CXD2880_IO_TGT_SYS,
+							  set_ts_output_seq1,
+							  ARRAY_SIZE(set_ts_output_seq1));
+			if (ret)
+				return ret;
+
+			ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+							  CXD2880_IO_TGT_DMD,
+							  set_ts_output_seq2,
+							  ARRAY_SIZE(set_ts_output_seq2));
+			if (ret)
+				return ret;
+		} else {
+			ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+							  CXD2880_IO_TGT_DMD,
+							  set_ts_output_seq3,
+							  ARRAY_SIZE(set_ts_output_seq3));
+			if (ret)
+				return ret;
+
+			ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+							  CXD2880_IO_TGT_SYS,
+							  set_ts_output_seq4,
+							  ARRAY_SIZE(set_ts_output_seq4));
+			if (ret)
+				return ret;
+		}
+		break;
+
+	case CXD2880_TNRDMD_TSOUT_IF_SPI:
+		break;
+
+	case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	switch (tnr_dmd->create_param.ts_output_if) {
+	case CXD2880_TNRDMD_TSOUT_IF_SPI:
+	case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, &data, 1);
+		if (ret)
+			return ret;
+
+		break;
+	case CXD2880_TNRDMD_TSOUT_IF_TS:
+	default:
+		break;
+	}
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x01, 0x01);
+}

+ 365 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h

@@ -0,0 +1,365 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_H
+#define CXD2880_TNRDMD_H
+
+#include <linux/atomic.h>
+
+#include "cxd2880_common.h"
+#include "cxd2880_io.h"
+#include "cxd2880_dtv.h"
+#include "cxd2880_dvbt.h"
+#include "cxd2880_dvbt2.h"
+
+#define CXD2880_TNRDMD_MAX_CFG_MEM_COUNT 100
+
+#define slvt_unfreeze_reg(tnr_dmd) ((void)((tnr_dmd)->io->write_reg\
+((tnr_dmd)->io, CXD2880_IO_TGT_DMD, 0x01, 0x00)))
+
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_UNDERFLOW     0x0001
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_OVERFLOW      0x0002
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_EMPTY  0x0004
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_FULL   0x0008
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_RRDY	  0x0010
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_COMMAND      0x0020
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_ACCESS       0x0040
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_CPU_ERROR	    0x0100
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_LOCK		 0x0200
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_INV_LOCK	     0x0400
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_NOOFDM	       0x0800
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_EWS		  0x1000
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_EEW		  0x2000
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_FEC_FAIL	     0x4000
+
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_L1POST_OK	0x01
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_DMD_LOCK	 0x02
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_TS_LOCK	  0x04
+
+enum cxd2880_tnrdmd_chip_id {
+	CXD2880_TNRDMD_CHIP_ID_UNKNOWN = 0x00,
+	CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X = 0x62,
+	CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11 = 0x6a
+};
+
+#define CXD2880_TNRDMD_CHIP_ID_VALID(chip_id) \
+	(((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) || \
+	 ((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11))
+
+enum cxd2880_tnrdmd_state {
+	CXD2880_TNRDMD_STATE_UNKNOWN,
+	CXD2880_TNRDMD_STATE_SLEEP,
+	CXD2880_TNRDMD_STATE_ACTIVE,
+	CXD2880_TNRDMD_STATE_INVALID
+};
+
+enum cxd2880_tnrdmd_divermode {
+	CXD2880_TNRDMD_DIVERMODE_SINGLE,
+	CXD2880_TNRDMD_DIVERMODE_MAIN,
+	CXD2880_TNRDMD_DIVERMODE_SUB
+};
+
+enum cxd2880_tnrdmd_clockmode {
+	CXD2880_TNRDMD_CLOCKMODE_UNKNOWN,
+	CXD2880_TNRDMD_CLOCKMODE_A,
+	CXD2880_TNRDMD_CLOCKMODE_B,
+	CXD2880_TNRDMD_CLOCKMODE_C
+};
+
+enum cxd2880_tnrdmd_tsout_if {
+	CXD2880_TNRDMD_TSOUT_IF_TS,
+	CXD2880_TNRDMD_TSOUT_IF_SPI,
+	CXD2880_TNRDMD_TSOUT_IF_SDIO
+};
+
+enum cxd2880_tnrdmd_xtal_share {
+	CXD2880_TNRDMD_XTAL_SHARE_NONE,
+	CXD2880_TNRDMD_XTAL_SHARE_EXTREF,
+	CXD2880_TNRDMD_XTAL_SHARE_MASTER,
+	CXD2880_TNRDMD_XTAL_SHARE_SLAVE
+};
+
+enum cxd2880_tnrdmd_spectrum_sense {
+	CXD2880_TNRDMD_SPECTRUM_NORMAL,
+	CXD2880_TNRDMD_SPECTRUM_INV
+};
+
+enum cxd2880_tnrdmd_cfg_id {
+	CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB,
+	CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI,
+	CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI,
+	CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI,
+	CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE,
+	CXD2880_TNRDMD_CFG_TSCLK_CONT,
+	CXD2880_TNRDMD_CFG_TSCLK_MASK,
+	CXD2880_TNRDMD_CFG_TSVALID_MASK,
+	CXD2880_TNRDMD_CFG_TSERR_MASK,
+	CXD2880_TNRDMD_CFG_TSERR_VALID_DIS,
+	CXD2880_TNRDMD_CFG_TSPIN_CURRENT,
+	CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL,
+	CXD2880_TNRDMD_CFG_TSPIN_PULLUP,
+	CXD2880_TNRDMD_CFG_TSCLK_FREQ,
+	CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL,
+	CXD2880_TNRDMD_CFG_TS_PACKET_GAP,
+	CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE,
+	CXD2880_TNRDMD_CFG_PWM_VALUE,
+	CXD2880_TNRDMD_CFG_INTERRUPT,
+	CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL,
+	CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL,
+	CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS,
+	CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS,
+	CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS,
+	CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE,
+	CXD2880_TNRDMD_CFG_CABLE_INPUT,
+	CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE,
+	CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE,
+	CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST,
+	CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
+	CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
+	CXD2880_TNRDMD_CFG_DVBT_PER_MES,
+	CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
+	CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
+	CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
+};
+
+enum cxd2880_tnrdmd_lock_result {
+	CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT,
+	CXD2880_TNRDMD_LOCK_RESULT_LOCKED,
+	CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED
+};
+
+enum cxd2880_tnrdmd_gpio_mode {
+	CXD2880_TNRDMD_GPIO_MODE_OUTPUT = 0x00,
+	CXD2880_TNRDMD_GPIO_MODE_INPUT = 0x01,
+	CXD2880_TNRDMD_GPIO_MODE_INT = 0x02,
+	CXD2880_TNRDMD_GPIO_MODE_FEC_FAIL = 0x03,
+	CXD2880_TNRDMD_GPIO_MODE_PWM = 0x04,
+	CXD2880_TNRDMD_GPIO_MODE_EWS = 0x05,
+	CXD2880_TNRDMD_GPIO_MODE_EEW = 0x06
+};
+
+enum cxd2880_tnrdmd_serial_ts_clk {
+	CXD2880_TNRDMD_SERIAL_TS_CLK_FULL,
+	CXD2880_TNRDMD_SERIAL_TS_CLK_HALF
+};
+
+struct cxd2880_tnrdmd_cfg_mem {
+	enum cxd2880_io_tgt tgt;
+	u8 bank;
+	u8 address;
+	u8 value;
+	u8 bit_mask;
+};
+
+struct cxd2880_tnrdmd_pid_cfg {
+	u8 is_en;
+	u16 pid;
+};
+
+struct cxd2880_tnrdmd_pid_ftr_cfg {
+	u8 is_negative;
+	struct cxd2880_tnrdmd_pid_cfg pid_cfg[32];
+};
+
+struct cxd2880_tnrdmd_lna_thrs {
+	u8 off_on;
+	u8 on_off;
+};
+
+struct cxd2880_tnrdmd_lna_thrs_tbl_air {
+	struct cxd2880_tnrdmd_lna_thrs thrs[24];
+};
+
+struct cxd2880_tnrdmd_lna_thrs_tbl_cable {
+	struct cxd2880_tnrdmd_lna_thrs thrs[32];
+};
+
+struct cxd2880_tnrdmd_create_param {
+	enum cxd2880_tnrdmd_tsout_if ts_output_if;
+	u8 en_internal_ldo;
+	enum cxd2880_tnrdmd_xtal_share xtal_share_type;
+	u8 xosc_cap;
+	u8 xosc_i;
+	u8 is_cxd2881gg;
+	u8 stationary_use;
+};
+
+struct cxd2880_tnrdmd_diver_create_param {
+	enum cxd2880_tnrdmd_tsout_if ts_output_if;
+	u8 en_internal_ldo;
+	u8 xosc_cap_main;
+	u8 xosc_i_main;
+	u8 xosc_i_sub;
+	u8 is_cxd2881gg;
+	u8 stationary_use;
+};
+
+struct cxd2880_tnrdmd {
+	struct cxd2880_tnrdmd *diver_sub;
+	struct cxd2880_io *io;
+	struct cxd2880_tnrdmd_create_param create_param;
+	enum cxd2880_tnrdmd_divermode diver_mode;
+	enum cxd2880_tnrdmd_clockmode fixed_clk_mode;
+	u8 is_cable_input;
+	u8 en_fef_intmtnt_base;
+	u8 en_fef_intmtnt_lite;
+	u8 blind_tune_dvbt2_first;
+	int (*rf_lvl_cmpstn)(struct cxd2880_tnrdmd *tnr_dmd,
+			     int *rf_lvl_db);
+	struct cxd2880_tnrdmd_lna_thrs_tbl_air *lna_thrs_tbl_air;
+	struct cxd2880_tnrdmd_lna_thrs_tbl_cable *lna_thrs_tbl_cable;
+	u8 srl_ts_clk_mod_cnts;
+	enum cxd2880_tnrdmd_serial_ts_clk srl_ts_clk_frq;
+	u8 ts_byte_clk_manual_setting;
+	u8 is_ts_backwards_compatible_mode;
+	struct cxd2880_tnrdmd_cfg_mem cfg_mem[CXD2880_TNRDMD_MAX_CFG_MEM_COUNT];
+	u8 cfg_mem_last_entry;
+	struct cxd2880_tnrdmd_pid_ftr_cfg pid_ftr_cfg;
+	u8 pid_ftr_cfg_en;
+	void *user;
+	enum cxd2880_tnrdmd_chip_id chip_id;
+	enum cxd2880_tnrdmd_state state;
+	enum cxd2880_tnrdmd_clockmode clk_mode;
+	u32 frequency_khz;
+	enum cxd2880_dtv_sys sys;
+	enum cxd2880_dtv_bandwidth bandwidth;
+	u8 scan_mode;
+	atomic_t cancel;
+};
+
+int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
+			  struct cxd2880_io *io,
+			  struct cxd2880_tnrdmd_create_param
+			  *create_param);
+
+int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
+				*tnr_dmd_main,
+				struct cxd2880_io *io_main,
+				struct cxd2880_tnrdmd *tnr_dmd_sub,
+				struct cxd2880_io *io_sub,
+				struct
+				cxd2880_tnrdmd_diver_create_param
+				*create_param);
+
+int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
+					     *tnr_dmd,
+					     u8 *task_completed);
+
+int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd
+					*tnr_dmd,
+					enum cxd2880_dtv_sys sys,
+					u32 frequency_khz,
+					enum cxd2880_dtv_bandwidth
+					bandwidth, u8 one_seg_opt,
+					u8 one_seg_opt_shft_dir);
+
+int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
+					*tnr_dmd,
+					enum cxd2880_dtv_sys sys,
+					u8 en_fef_intmtnt_ctrl);
+
+int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+			   enum cxd2880_tnrdmd_cfg_id id,
+			   int value);
+
+int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+				u8 id,
+				u8 en,
+				enum cxd2880_tnrdmd_gpio_mode mode,
+				u8 open_drain, u8 invert);
+
+int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				    u8 id,
+				    u8 en,
+				    enum cxd2880_tnrdmd_gpio_mode
+				    mode, u8 open_drain,
+				    u8 invert);
+
+int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
+			     u8 id, u8 *value);
+
+int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				 u8 id, u8 *value);
+
+int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+			      u8 id, u8 value);
+
+int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				  u8 id, u8 value);
+
+int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
+				  u16 *value);
+
+int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
+				   u16 value);
+
+int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
+				u8 clear_overflow_flag,
+				u8 clear_underflow_flag,
+				u8 clear_buf);
+
+int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
+			   enum cxd2880_tnrdmd_chip_id *chip_id);
+
+int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
+					 *tnr_dmd,
+					 enum cxd2880_io_tgt tgt,
+					 u8 bank, u8 address,
+					 u8 value, u8 bit_mask);
+
+int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
+				 enum cxd2880_dtv_sys sys,
+				 u8 scan_mode_end);
+
+int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_tnrdmd_pid_ftr_cfg
+			       *pid_ftr_cfg);
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     int (*rf_lvl_cmpstn)
+				     (struct cxd2880_tnrdmd *,
+				     int *));
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd *tnr_dmd,
+					 int (*rf_lvl_cmpstn)
+					 (struct cxd2880_tnrdmd *,
+					 int *));
+
+int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
+				struct
+				cxd2880_tnrdmd_lna_thrs_tbl_air
+				*tbl_air,
+				struct
+				cxd2880_tnrdmd_lna_thrs_tbl_cable
+				*tbl_cable);
+
+int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				    struct
+				    cxd2880_tnrdmd_lna_thrs_tbl_air
+				    *tbl_air,
+				    struct
+				    cxd2880_tnrdmd_lna_thrs_tbl_cable
+				    *tbl_cable);
+
+int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
+				       *tnr_dmd, u8 en, u8 value);
+
+int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
+				 u8 en);
+
+int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd);
+
+#endif

+ 12 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h

@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_driver_version.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * version information
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.4"
+
+#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2018-01-17"

+ 919 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c

@@ -0,0 +1,919 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <media/dvb_frontend.h>
+
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = {
+	{0x00, 0x00}, {0x31, 0x01},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = {
+	{0x00, 0x04}, {0x5c, 0xfb}, {0x00, 0x10}, {0xa4, 0x03},
+	{0x00, 0x14}, {0xb0, 0x00}, {0x00, 0x25},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq3[] = {
+	{0x00, 0x12}, {0x44, 0x00},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq4[] = {
+	{0x00, 0x11}, {0x87, 0xd2},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq5[] = {
+	{0x00, 0x00}, {0xfd, 0x01},
+};
+
+static const struct cxd2880_reg_value sleep_dmd_setting_seq1[] = {
+	{0x00, 0x04}, {0x5c, 0xd8}, {0x00, 0x10}, {0xa4, 0x00},
+};
+
+static const struct cxd2880_reg_value sleep_dmd_setting_seq2[] = {
+	{0x00, 0x11}, {0x87, 0x04},
+};
+
+static int x_tune_dvbt_demod_setting(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     enum cxd2880_dtv_bandwidth
+				     bandwidth,
+				     enum cxd2880_tnrdmd_clockmode
+				     clk_mode)
+{
+	static const u8 clk_mode_ckffrq_a[2] = { 0x52, 0x49 };
+	static const u8 clk_mode_ckffrq_b[2] = { 0x5d, 0x55 };
+	static const u8 clk_mode_ckffrq_c[2] = { 0x60, 0x00 };
+	static const u8 ratectl_margin[2] = { 0x01, 0xf0 };
+	static const u8 maxclkcnt_a[3] = { 0x73, 0xca, 0x49 };
+	static const u8 maxclkcnt_b[3] = { 0xc8, 0x13, 0xaa };
+	static const u8 maxclkcnt_c[3] = { 0xdc, 0x6c, 0x00 };
+
+	static const u8 bw8_nomi_ac[5] = { 0x15, 0x00, 0x00, 0x00, 0x00};
+	static const u8 bw8_nomi_b[5] = { 0x14, 0x6a, 0xaa, 0xaa, 0xaa};
+	static const u8 bw8_gtdofst_a[2] = { 0x01, 0x28 };
+	static const u8 bw8_gtdofst_b[2] = { 0x11, 0x44 };
+	static const u8 bw8_gtdofst_c[2] = { 0x15, 0x28 };
+	static const u8 bw8_mrc_a[5] = { 0x30, 0x00, 0x00, 0x90, 0x00 };
+	static const u8 bw8_mrc_b[5] = { 0x36, 0x71, 0x00, 0xa3, 0x55 };
+	static const u8 bw8_mrc_c[5] = { 0x38, 0x00, 0x00, 0xa8, 0x00 };
+	static const u8 bw8_notch[4] = { 0xb3, 0x00, 0x01, 0x02 };
+
+	static const u8 bw7_nomi_ac[5] = { 0x18, 0x00, 0x00, 0x00, 0x00};
+	static const u8 bw7_nomi_b[5] = { 0x17, 0x55, 0x55, 0x55, 0x55};
+	static const u8 bw7_gtdofst_a[2] = { 0x12, 0x4c };
+	static const u8 bw7_gtdofst_b[2] = { 0x1f, 0x15 };
+	static const u8 bw7_gtdofst_c[2] = { 0x1f, 0xf8 };
+	static const u8 bw7_mrc_a[5] = { 0x36, 0xdb, 0x00, 0xa4, 0x92 };
+	static const u8 bw7_mrc_b[5] = { 0x3e, 0x38, 0x00, 0xba, 0xaa };
+	static const u8 bw7_mrc_c[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 };
+	static const u8 bw7_notch[4] = { 0xb8, 0x00, 0x00, 0x03 };
+
+	static const u8 bw6_nomi_ac[5] = { 0x1c, 0x00, 0x00, 0x00, 0x00};
+	static const u8 bw6_nomi_b[5] = { 0x1b, 0x38, 0xe3, 0x8e, 0x38};
+	static const u8 bw6_gtdofst_a[2] = { 0x1f, 0xf8 };
+	static const u8 bw6_gtdofst_b[2] = { 0x24, 0x43 };
+	static const u8 bw6_gtdofst_c[2] = { 0x25, 0x4c };
+	static const u8 bw6_mrc_a[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 };
+	static const u8 bw6_mrc_b[5] = { 0x48, 0x97, 0x00, 0xd9, 0xc7 };
+	static const u8 bw6_mrc_c[5] = { 0x4a, 0xaa, 0x00, 0xdf, 0xff };
+	static const u8 bw6_notch[4] = { 0xbe, 0xab, 0x00, 0x03 };
+
+	static const u8 bw5_nomi_ac[5] = { 0x21, 0x99, 0x99, 0x99, 0x99};
+	static const u8 bw5_nomi_b[5] = { 0x20, 0xaa, 0xaa, 0xaa, 0xaa};
+	static const u8 bw5_gtdofst_a[2] = { 0x26, 0x5d };
+	static const u8 bw5_gtdofst_b[2] = { 0x2b, 0x84 };
+	static const u8 bw5_gtdofst_c[2] = { 0x2c, 0xc2 };
+	static const u8 bw5_mrc_a[5] = { 0x4c, 0xcc, 0x00, 0xe6, 0x66 };
+	static const u8 bw5_mrc_b[5] = { 0x57, 0x1c, 0x01, 0x05, 0x55 };
+	static const u8 bw5_mrc_c[5] = { 0x59, 0x99, 0x01, 0x0c, 0xcc };
+	static const u8 bw5_notch[4] = { 0xc8, 0x01, 0x00, 0x03 };
+	const u8 *data = NULL;
+	u8 sst_data;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  tune_dmd_setting_seq1,
+					  ARRAY_SIZE(tune_dmd_setting_seq1));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret)
+		return ret;
+
+	switch (clk_mode) {
+	case CXD2880_TNRDMD_CLOCKMODE_A:
+		data = clk_mode_ckffrq_a;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_B:
+		data = clk_mode_ckffrq_b;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_C:
+		data = clk_mode_ckffrq_c;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x65, data, 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x5d, 0x07);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		u8 data[2] = { 0x01, 0x01 };
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x00);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0xce, data, 2);
+		if (ret)
+			return ret;
+	}
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  tune_dmd_setting_seq2,
+					  ARRAY_SIZE(tune_dmd_setting_seq2));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xf0, ratectl_margin, 2);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN ||
+	    tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+						  CXD2880_IO_TGT_DMD,
+						  tune_dmd_setting_seq3,
+						  ARRAY_SIZE(tune_dmd_setting_seq3));
+		if (ret)
+			return ret;
+	}
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+						  CXD2880_IO_TGT_DMD,
+						  tune_dmd_setting_seq4,
+						  ARRAY_SIZE(tune_dmd_setting_seq4));
+		if (ret)
+			return ret;
+	}
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x04);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = maxclkcnt_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = maxclkcnt_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = maxclkcnt_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x68, data, 3);
+		if (ret)
+			return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret)
+		return ret;
+
+	switch (bandwidth) {
+	case CXD2880_DTV_BW_8_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw8_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw8_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x60, data, 5);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x00);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw8_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw8_gtdofst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw8_gtdofst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x7d, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			sst_data = 0x35;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			sst_data = 0x34;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x71, sst_data);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw8_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw8_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw8_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, &data[0], 2);
+			if (ret)
+				return ret;
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x51, &data[2], 3);
+			if (ret)
+				return ret;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x72, &bw8_notch[0], 2);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x6b, &bw8_notch[2], 2);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_DTV_BW_7_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw7_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw7_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x60, data, 5);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x02);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw7_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw7_gtdofst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw7_gtdofst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x7d, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			sst_data = 0x2f;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			sst_data = 0x2e;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x71, sst_data);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw7_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw7_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw7_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, &data[0], 2);
+			if (ret)
+				return ret;
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x51, &data[2], 3);
+			if (ret)
+				return ret;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x72, &bw7_notch[0], 2);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x6b, &bw7_notch[2], 2);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_DTV_BW_6_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw6_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw6_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x60, data, 5);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x04);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw6_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw6_gtdofst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw6_gtdofst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x7d, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			sst_data = 0x29;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			sst_data = 0x2a;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x71, sst_data);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw6_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw6_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw6_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, &data[0], 2);
+			if (ret)
+				return ret;
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x51, &data[2], 3);
+			if (ret)
+				return ret;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x72, &bw6_notch[0], 2);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x6b, &bw6_notch[2], 2);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_DTV_BW_5_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw5_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw5_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x60, data, 5);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x06);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw5_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw5_gtdofst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw5_gtdofst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x7d, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			sst_data = 0x24;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			sst_data = 0x23;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x71, sst_data);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw5_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw5_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw5_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, &data[0], 2);
+			if (ret)
+				return ret;
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x51, &data[2], 3);
+			if (ret)
+				return ret;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x72, &bw5_notch[0], 2);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x6b, &bw5_notch[2], 2);
+		if (ret)
+			return ret;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return cxd2880_io_write_multi_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD,
+					   tune_dmd_setting_seq5,
+					   ARRAY_SIZE(tune_dmd_setting_seq5));
+}
+
+static int x_sleep_dvbt_demod_setting(struct cxd2880_tnrdmd
+						   *tnr_dmd)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  sleep_dmd_setting_seq1,
+					  ARRAY_SIZE(sleep_dmd_setting_seq1));
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+						  CXD2880_IO_TGT_DMD,
+						  sleep_dmd_setting_seq2,
+						  ARRAY_SIZE(sleep_dmd_setting_seq2));
+
+	return ret;
+}
+
+static int dvbt_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+			    enum cxd2880_dvbt_profile profile)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x67,
+				      (profile == CXD2880_DVBT_PROFILE_HP)
+				      ? 0x00 : 0x01);
+}
+
+int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt_tune_param
+			      *tune_param)
+{
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+						tune_param->center_freq_khz,
+						tune_param->bandwidth, 0, 0);
+	if (ret)
+		return ret;
+
+	ret =
+	    x_tune_dvbt_demod_setting(tnr_dmd, tune_param->bandwidth,
+				      tnr_dmd->clk_mode);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    x_tune_dvbt_demod_setting(tnr_dmd->diver_sub,
+					      tune_param->bandwidth,
+					      tnr_dmd->diver_sub->clk_mode);
+		if (ret)
+			return ret;
+	}
+
+	return dvbt_set_profile(tnr_dmd, tune_param->profile);
+}
+
+int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt_tune_param
+			      *tune_param)
+{
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+						0);
+	if (ret)
+		return ret;
+
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+	tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+	tnr_dmd->sys = CXD2880_DTV_SYS_DVBT;
+	tnr_dmd->bandwidth = tune_param->bandwidth;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+		tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT;
+		tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = x_sleep_dvbt_demod_setting(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+		ret = x_sleep_dvbt_demod_setting(tnr_dmd->diver_sub);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+					 *tnr_dmd,
+					 enum
+					 cxd2880_tnrdmd_lock_result
+					 *lock)
+{
+	int ret;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if (!tnr_dmd || !lock)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					      &unlock_detected);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (sync_stat == 6)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (sync_stat == 6) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						  &unlock_detected_sub);
+	if (ret)
+		return ret;
+
+	if (sync_stat == 6)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+	else if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+				      *tnr_dmd,
+				      enum
+				      cxd2880_tnrdmd_lock_result
+				      *lock)
+{
+	int ret;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if (!tnr_dmd || !lock)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					      &unlock_detected);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (ts_lock)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (ts_lock) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	} else if (!unlock_detected) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						  &unlock_detected_sub);
+	if (ret)
+		return ret;
+
+	if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}

+ 45 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h

@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT_H
+#define CXD2880_TNRDMD_DVBT_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+struct cxd2880_dvbt_tune_param {
+	u32 center_freq_khz;
+	enum cxd2880_dtv_bandwidth bandwidth;
+	enum cxd2880_dvbt_profile profile;
+};
+
+int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt_tune_param
+			      *tune_param);
+
+int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt_tune_param
+			      *tune_param);
+
+int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd
+				      *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+					 *tnr_dmd,
+					 enum
+					 cxd2880_tnrdmd_lock_result
+					 *lock);
+
+int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+				      *tnr_dmd,
+				      enum
+				      cxd2880_tnrdmd_lock_result
+				      *lock);
+
+#endif

+ 1217 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c

@@ -0,0 +1,1217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt2.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T2
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <media/dvb_frontend.h>
+
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = {
+	{0x00, 0x00}, {0x31, 0x02},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = {
+	{0x00, 0x04}, {0x5d, 0x0b},
+};
+
+static int x_tune_dvbt2_demod_setting(struct cxd2880_tnrdmd
+				      *tnr_dmd,
+				      enum cxd2880_dtv_bandwidth
+				      bandwidth,
+				      enum cxd2880_tnrdmd_clockmode
+				      clk_mode)
+{
+	static const u8 tsif_settings[2] = { 0x01, 0x01 };
+	static const u8 init_settings[14] = {
+		0x07, 0x06, 0x01, 0xf0,	0x00, 0x00, 0x04, 0xb0, 0x00, 0x00,
+		0x09, 0x9c, 0x0e, 0x4c
+	};
+	static const u8 clk_mode_settings_a1[9] = {
+		0x52, 0x49, 0x2c, 0x51,	0x51, 0x3d, 0x15, 0x29, 0x0c
+	};
+
+	static const u8 clk_mode_settings_b1[9] = {
+		0x5d, 0x55, 0x32, 0x5c,	0x5c, 0x45, 0x17, 0x2e, 0x0d
+	};
+
+	static const u8 clk_mode_settings_c1[9] = {
+		0x60, 0x00, 0x34, 0x5e,	0x5e, 0x47, 0x18, 0x2f, 0x0e
+	};
+
+	static const u8 clk_mode_settings_a2[13] = {
+		0x04, 0xe7, 0x94, 0x92,	0x09, 0xcf, 0x7e, 0xd0, 0x49,
+		0xcd, 0xcd, 0x1f, 0x5b
+	};
+
+	static const u8 clk_mode_settings_b2[13] = {
+		0x05, 0x90, 0x27, 0x55,	0x0b, 0x20, 0x8f, 0xd6, 0xea,
+		0xc8, 0xc8, 0x23, 0x91
+	};
+
+	static const u8 clk_mode_settings_c2[13] = {
+		0x05, 0xb8, 0xd8, 0x00,	0x0b, 0x72, 0x93, 0xf3, 0x00,
+		0xcd, 0xcd, 0x24, 0x95
+	};
+
+	static const u8 clk_mode_settings_a3[5] = {
+		0x0b, 0x6a, 0xc9, 0x03, 0x33
+	};
+	static const u8 clk_mode_settings_b3[5] = {
+		0x01, 0x02, 0xe4, 0x03, 0x39
+	};
+	static const u8 clk_mode_settings_c3[5] = {
+		0x01, 0x02, 0xeb, 0x03, 0x3b
+	};
+
+	static const u8 gtdofst[2] = { 0x3f, 0xff };
+
+	static const u8 bw8_gtdofst_a[2] = { 0x19, 0xd2 };
+	static const u8 bw8_nomi_ac[6] = { 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	static const u8 bw8_nomi_b[6] = { 0x14, 0x6a, 0xaa, 0xaa, 0xab, 0x00 };
+	static const u8 bw8_sst_a[2] = { 0x06, 0x2a };
+	static const u8 bw8_sst_b[2] = { 0x06, 0x29 };
+	static const u8 bw8_sst_c[2] = { 0x06, 0x28 };
+	static const u8 bw8_mrc_a[9] = {
+		0x28, 0x00, 0x50, 0x00, 0x60, 0x00, 0x00, 0x90, 0x00
+	};
+	static const u8 bw8_mrc_b[9] = {
+		0x2d, 0x5e, 0x5a, 0xbd, 0x6c, 0xe3, 0x00, 0xa3, 0x55
+	};
+	static const u8 bw8_mrc_c[9] = {
+		0x2e, 0xaa, 0x5d, 0x55, 0x70, 0x00, 0x00, 0xa8, 0x00
+	};
+
+	static const u8 bw7_nomi_ac[6] = { 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	static const u8 bw7_nomi_b[6] = { 0x17, 0x55, 0x55, 0x55, 0x55, 0x00 };
+	static const u8 bw7_sst_a[2] = { 0x06, 0x23 };
+	static const u8 bw7_sst_b[2] = { 0x06, 0x22 };
+	static const u8 bw7_sst_c[2] = { 0x06, 0x21 };
+	static const u8 bw7_mrc_a[9] = {
+		0x2d, 0xb6, 0x5b, 0x6d,	0x6d, 0xb6, 0x00, 0xa4, 0x92
+	};
+	static const u8 bw7_mrc_b[9] = {
+		0x33, 0xda, 0x67, 0xb4,	0x7c, 0x71, 0x00, 0xba, 0xaa
+	};
+	static const u8 bw7_mrc_c[9] = {
+		0x35, 0x55, 0x6a, 0xaa,	0x80, 0x00, 0x00, 0xc0, 0x00
+	};
+
+	static const u8 bw6_nomi_ac[6] = { 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	static const u8 bw6_nomi_b[6] = { 0x1b, 0x38, 0xe3, 0x8e, 0x39, 0x00 };
+	static const u8 bw6_sst_a[2] = { 0x06, 0x1c };
+	static const u8 bw6_sst_b[2] = { 0x06, 0x1b };
+	static const u8 bw6_sst_c[2] = { 0x06, 0x1a };
+	static const u8 bw6_mrc_a[9] = {
+		0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00
+	};
+	static const u8 bw6_mrc_b[9] = {
+		0x3c, 0x7e, 0x78, 0xfc,	0x91, 0x2f, 0x00, 0xd9, 0xc7
+	};
+	static const u8 bw6_mrc_c[9] = {
+		0x3e, 0x38, 0x7c, 0x71,	0x95, 0x55, 0x00, 0xdf, 0xff
+	};
+
+	static const u8 bw5_nomi_ac[6] = { 0x21, 0x99, 0x99, 0x99, 0x9a, 0x00 };
+	static const u8 bw5_nomi_b[6] = { 0x20, 0xaa, 0xaa, 0xaa, 0xab, 0x00 };
+	static const u8 bw5_sst_a[2] = { 0x06, 0x15 };
+	static const u8 bw5_sst_b[2] = { 0x06, 0x15 };
+	static const u8 bw5_sst_c[2] = { 0x06, 0x14 };
+	static const u8 bw5_mrc_a[9] = {
+		0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xe6, 0x66
+	};
+	static const u8 bw5_mrc_b[9] = {
+		0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x01, 0x05, 0x55
+	};
+	static const u8 bw5_mrc_c[9] = {
+		0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x01, 0x0c, 0xcc
+	};
+
+	static const u8 bw1_7_nomi_a[6] = {
+		0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03
+	};
+	static const u8 bw1_7_nomi_c[6] = {
+		0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03
+	};
+	static const u8 bw1_7_nomi_b[6] = {
+		0x65, 0x2b, 0xa4, 0xcd, 0xd8, 0x03
+	};
+	static const u8 bw1_7_sst_a[2] = { 0x06, 0x0c };
+	static const u8 bw1_7_sst_b[2] = { 0x06, 0x0c };
+	static const u8 bw1_7_sst_c[2] = { 0x06, 0x0b };
+	static const u8 bw1_7_mrc_a[9] = {
+		0x40, 0x00, 0x6a, 0xaa,	0x80, 0x00, 0x02, 0xc9, 0x8f
+	};
+	static const u8 bw1_7_mrc_b[9] = {
+		0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x03, 0x29, 0x5d
+	};
+	static const u8 bw1_7_mrc_c[9] = {
+		0x4a, 0xaa, 0x7c, 0x71,	0x95, 0x55, 0x03, 0x40, 0x7d
+	};
+
+	const u8 *data = NULL;
+	const u8 *data2 = NULL;
+	const u8 *data3 = NULL;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  tune_dmd_setting_seq1,
+					  ARRAY_SIZE(tune_dmd_setting_seq1));
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  tune_dmd_setting_seq2,
+					  ARRAY_SIZE(tune_dmd_setting_seq2));
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x00);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0xce, tsif_settings, 2);
+		if (ret)
+			return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x20);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x8a, init_settings[0]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x90, init_settings[1]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x25);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xf0, &init_settings[2], 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x2a);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xdc, init_settings[4]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xde, init_settings[5]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x2d);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x73, &init_settings[6], 4);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x8f, &init_settings[10], 4);
+	if (ret)
+		return ret;
+
+	switch (clk_mode) {
+	case CXD2880_TNRDMD_CLOCKMODE_A:
+		data = clk_mode_settings_a1;
+		data2 = clk_mode_settings_a2;
+		data3 = clk_mode_settings_a3;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_B:
+		data = clk_mode_settings_b1;
+		data2 = clk_mode_settings_b2;
+		data3 = clk_mode_settings_b3;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_C:
+		data = clk_mode_settings_c1;
+		data2 = clk_mode_settings_c2;
+		data3 = clk_mode_settings_c3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x1d, &data[0], 3);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x22, data[3]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x24, data[4]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x26, data[5]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x29, &data[6], 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x2d, data[8]);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x2e, &data2[0], 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x35, &data2[6], 7);
+		if (ret)
+			return ret;
+	}
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x3c, &data3[0], 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x56, &data3[2], 3);
+	if (ret)
+		return ret;
+
+	switch (bandwidth) {
+	case CXD2880_DTV_BW_8_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw8_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw8_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x00);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw8_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = gtdofst;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw8_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw8_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw8_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw8_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw8_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw8_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	case CXD2880_DTV_BW_7_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw7_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw7_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x02);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, gtdofst, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw7_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw7_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw7_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw7_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw7_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw7_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	case CXD2880_DTV_BW_6_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw6_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw6_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x04);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, gtdofst, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw6_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw6_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw6_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw6_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw6_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw6_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	case CXD2880_DTV_BW_5_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw5_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw5_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x06);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, gtdofst, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw5_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw5_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw5_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw5_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw5_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw5_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	case CXD2880_DTV_BW_1_7_MHZ:
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw1_7_nomi_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw1_7_nomi_c;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw1_7_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x03);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, gtdofst, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw1_7_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw1_7_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw1_7_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw1_7_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw1_7_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw1_7_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xfd, 0x01);
+}
+
+static int x_sleep_dvbt2_demod_setting(struct cxd2880_tnrdmd
+				       *tnr_dmd)
+{
+	static const u8 difint_clip[] = {
+		0, 1, 0, 2, 0, 4, 0, 8, 0, 16, 0, 32
+	};
+	int ret = 0;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x1d);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x47, difint_clip, 12);
+	}
+
+	return ret;
+}
+
+static int dvbt2_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+			     enum cxd2880_dvbt2_profile profile)
+{
+	u8 t2_mode_tune_mode = 0;
+	u8 seq_not2_dtime = 0;
+	u8 dtime1 = 0;
+	u8 dtime2 = 0;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	switch (tnr_dmd->clk_mode) {
+	case CXD2880_TNRDMD_CLOCKMODE_A:
+		dtime1 = 0x27;
+		dtime2 = 0x0c;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_B:
+		dtime1 = 0x2c;
+		dtime2 = 0x0d;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_C:
+		dtime1 = 0x2e;
+		dtime2 = 0x0e;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (profile) {
+	case CXD2880_DVBT2_PROFILE_BASE:
+		t2_mode_tune_mode = 0x01;
+		seq_not2_dtime = dtime2;
+		break;
+
+	case CXD2880_DVBT2_PROFILE_LITE:
+		t2_mode_tune_mode = 0x05;
+		seq_not2_dtime = dtime1;
+		break;
+
+	case CXD2880_DVBT2_PROFILE_ANY:
+		t2_mode_tune_mode = 0x00;
+		seq_not2_dtime = dtime1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x2e);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x10, t2_mode_tune_mode);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret)
+		return ret;
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x2c, seq_not2_dtime);
+}
+
+int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_dvbt2_tune_param
+			       *tune_param)
+{
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN &&
+	    tune_param->profile == CXD2880_DVBT2_PROFILE_ANY)
+		return -ENOTTY;
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT2,
+						tune_param->center_freq_khz,
+						tune_param->bandwidth, 0, 0);
+	if (ret)
+		return ret;
+
+	ret =
+	    x_tune_dvbt2_demod_setting(tnr_dmd, tune_param->bandwidth,
+				       tnr_dmd->clk_mode);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    x_tune_dvbt2_demod_setting(tnr_dmd->diver_sub,
+					       tune_param->bandwidth,
+					       tnr_dmd->diver_sub->clk_mode);
+		if (ret)
+			return ret;
+	}
+
+	ret = dvbt2_set_profile(tnr_dmd, tune_param->profile);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    dvbt2_set_profile(tnr_dmd->diver_sub, tune_param->profile);
+		if (ret)
+			return ret;
+	}
+
+	if (tune_param->data_plp_id == CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO)
+		ret = cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 1, 0);
+	else
+		ret =
+		    cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 0,
+					     (u8)(tune_param->data_plp_id));
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_dvbt2_tune_param
+			       *tune_param)
+{
+	u8 en_fef_intmtnt_ctrl = 1;
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	switch (tune_param->profile) {
+	case CXD2880_DVBT2_PROFILE_BASE:
+		en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_base;
+		break;
+	case CXD2880_DVBT2_PROFILE_LITE:
+		en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_lite;
+		break;
+	case CXD2880_DVBT2_PROFILE_ANY:
+		if (tnr_dmd->en_fef_intmtnt_base &&
+		    tnr_dmd->en_fef_intmtnt_lite)
+			en_fef_intmtnt_ctrl = 1;
+		else
+			en_fef_intmtnt_ctrl = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting2(tnr_dmd,
+						CXD2880_DTV_SYS_DVBT2,
+						en_fef_intmtnt_ctrl);
+	if (ret)
+		return ret;
+
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+	tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+	tnr_dmd->sys = CXD2880_DTV_SYS_DVBT2;
+	tnr_dmd->bandwidth = tune_param->bandwidth;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+		tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT2;
+		tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+				       *tnr_dmd)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = x_sleep_dvbt2_demod_setting(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+		ret = x_sleep_dvbt2_demod_setting(tnr_dmd->diver_sub);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+					  *tnr_dmd,
+					  enum
+					  cxd2880_tnrdmd_lock_result
+					  *lock)
+{
+	int ret;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if (!tnr_dmd || !lock)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					       &unlock_detected);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (sync_stat == 6)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (sync_stat == 6) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						   &unlock_detected_sub);
+	if (ret)
+		return ret;
+
+	if (sync_stat == 6)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+	else if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+				       *tnr_dmd,
+				       enum
+				       cxd2880_tnrdmd_lock_result
+				       *lock)
+{
+	int ret;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if (!tnr_dmd || !lock)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					       &unlock_detected);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (ts_lock)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (ts_lock) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	} else if (!unlock_detected) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						   &unlock_detected_sub);
+	if (ret)
+		return ret;
+
+	if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+				     *tnr_dmd, u8 auto_plp,
+				     u8 plp_id)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x23);
+	if (ret)
+		return ret;
+
+	if (!auto_plp) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0xaf, plp_id);
+		if (ret)
+			return ret;
+	}
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xad, auto_plp ? 0x00 : 0x01);
+}
+
+int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+					   *tnr_dmd)
+{
+	struct cxd2880_dvbt2_ofdm ofdm;
+	static const u8 data[] = { 0, 8, 0, 16, 0, 32, 0, 64, 0, 128, 1, 0};
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE)
+		return 0;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd, &ofdm);
+	if (ret)
+		return ret;
+
+	if (!ofdm.mixed)
+		return 0;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x1d);
+	if (ret)
+		return ret;
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_DMD,
+				       0x47, data, 12);
+}
+
+int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+					    *tnr_dmd,
+					    u8 *l1_post_valid)
+{
+	int ret;
+
+	u8 data;
+
+	if (!tnr_dmd || !l1_post_valid)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x86, &data, 1);
+	if (ret)
+		return ret;
+
+	*l1_post_valid = data & 0x01;
+
+	return ret;
+}

+ 65 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h

@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T2
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT2_H
+#define CXD2880_TNRDMD_DVBT2_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+enum cxd2880_tnrdmd_dvbt2_tune_info {
+	CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK,
+	CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID
+};
+
+struct cxd2880_dvbt2_tune_param {
+	u32 center_freq_khz;
+	enum cxd2880_dtv_bandwidth bandwidth;
+	u16 data_plp_id;
+	enum cxd2880_dvbt2_profile profile;
+	enum cxd2880_tnrdmd_dvbt2_tune_info tune_info;
+};
+
+#define CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO  0xffff
+
+int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_dvbt2_tune_param
+			       *tune_param);
+
+int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_dvbt2_tune_param
+			       *tune_param);
+
+int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+				       *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+					  *tnr_dmd,
+					  enum
+					  cxd2880_tnrdmd_lock_result
+					  *lock);
+
+int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+				       *tnr_dmd,
+				       enum
+				       cxd2880_tnrdmd_lock_result
+				       *lock);
+
+int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+				     *tnr_dmd, u8 auto_plp,
+				     u8 plp_id);
+
+int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+					   *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+					    *tnr_dmd,
+					    u8 *l1_post_valid);
+
+#endif

+ 1878 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c

@@ -0,0 +1,1878 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+
+#include <media/dvb_math.h>
+
+static const int ref_dbm_1000[4][8] = {
+	{-96000, -95000, -94000, -93000, -92000, -92000, -98000, -97000},
+	{-91000, -89000, -88000, -87000, -86000, -86000, -93000, -92000},
+	{-86000, -85000, -83000, -82000, -81000, -80000, -89000, -88000},
+	{-82000, -80000, -78000, -76000, -75000, -74000, -86000, -84000},
+};
+
+int cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd
+				       *tnr_dmd, u8 *sync_stat,
+				       u8 *ts_lock_stat,
+				       u8 *unlock_detected)
+{
+	u8 data;
+	int ret;
+
+	if (!tnr_dmd || !sync_stat || !ts_lock_stat || !unlock_detected)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x10, &data, sizeof(data));
+	if (ret)
+		return ret;
+
+	*sync_stat = data & 0x07;
+	*ts_lock_stat = ((data & 0x20) ? 1 : 0);
+	*unlock_detected = ((data & 0x10) ? 1 : 0);
+
+	if (*sync_stat == 0x07)
+		return -EAGAIN;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd
+					   *tnr_dmd,
+					   u8 *sync_stat,
+					   u8 *unlock_detected)
+{
+	u8 ts_lock_stat = 0;
+
+	if (!tnr_dmd || !sync_stat || !unlock_detected)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd->diver_sub,
+						  sync_stat,
+						  &ts_lock_stat,
+						  unlock_detected);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd
+					    *tnr_dmd, int *offset)
+{
+	u8 data[4];
+	u32 ctl_val = 0;
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	int ret;
+
+	if (!tnr_dmd || !offset)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+					       &ts_lock,
+					       &unlock_detected);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (sync_state != 6) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x30, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	ctl_val =
+	    ((data[0] & 0x0f) << 24) | (data[1] << 16) | (data[2] << 8)
+	    | (data[3]);
+	*offset = cxd2880_convert2s_complement(ctl_val, 28);
+
+	switch (tnr_dmd->bandwidth) {
+	case CXD2880_DTV_BW_1_7_MHZ:
+		*offset = -1 * ((*offset) / 582);
+		break;
+	case CXD2880_DTV_BW_5_MHZ:
+	case CXD2880_DTV_BW_6_MHZ:
+	case CXD2880_DTV_BW_7_MHZ:
+	case CXD2880_DTV_BW_8_MHZ:
+		*offset = -1 * ((*offset) * tnr_dmd->bandwidth / 940);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct
+						cxd2880_tnrdmd
+						*tnr_dmd,
+						int *offset)
+{
+	if (!tnr_dmd || !offset)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_dvbt2_mon_carrier_offset(tnr_dmd->diver_sub,
+						       offset);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd,
+				    struct cxd2880_dvbt2_l1pre
+				    *l1_pre)
+{
+	u8 data[37];
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 version = 0;
+	enum cxd2880_dvbt2_profile profile;
+	int ret;
+
+	if (!tnr_dmd || !l1_pre)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+					       &ts_lock,
+					       &unlock_detected);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (sync_state < 5) {
+		if (tnr_dmd->diver_mode ==
+		    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret =
+			    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+			    (tnr_dmd, &sync_state, &unlock_detected);
+			if (ret) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return ret;
+			}
+
+			if (sync_state < 5) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return -EAGAIN;
+			}
+		} else {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EAGAIN;
+		}
+	}
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd, &profile);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x61, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+	slvt_unfreeze_reg(tnr_dmd);
+
+	l1_pre->type = (enum cxd2880_dvbt2_l1pre_type)data[0];
+	l1_pre->bw_ext = data[1] & 0x01;
+	l1_pre->s1 = (enum cxd2880_dvbt2_s1)(data[2] & 0x07);
+	l1_pre->s2 = data[3] & 0x0f;
+	l1_pre->l1_rep = data[4] & 0x01;
+	l1_pre->gi = (enum cxd2880_dvbt2_guard)(data[5] & 0x07);
+	l1_pre->papr = (enum cxd2880_dvbt2_papr)(data[6] & 0x0f);
+	l1_pre->mod =
+	    (enum cxd2880_dvbt2_l1post_constell)(data[7] & 0x0f);
+	l1_pre->cr = (enum cxd2880_dvbt2_l1post_cr)(data[8] & 0x03);
+	l1_pre->fec =
+	    (enum cxd2880_dvbt2_l1post_fec_type)(data[9] & 0x03);
+	l1_pre->l1_post_size = (data[10] & 0x03) << 16;
+	l1_pre->l1_post_size |= (data[11]) << 8;
+	l1_pre->l1_post_size |= (data[12]);
+	l1_pre->l1_post_info_size = (data[13] & 0x03) << 16;
+	l1_pre->l1_post_info_size |= (data[14]) << 8;
+	l1_pre->l1_post_info_size |= (data[15]);
+	l1_pre->pp = (enum cxd2880_dvbt2_pp)(data[16] & 0x0f);
+	l1_pre->tx_id_availability = data[17];
+	l1_pre->cell_id = (data[18] << 8);
+	l1_pre->cell_id |= (data[19]);
+	l1_pre->network_id = (data[20] << 8);
+	l1_pre->network_id |= (data[21]);
+	l1_pre->sys_id = (data[22] << 8);
+	l1_pre->sys_id |= (data[23]);
+	l1_pre->num_frames = data[24];
+	l1_pre->num_symbols = (data[25] & 0x0f) << 8;
+	l1_pre->num_symbols |= data[26];
+	l1_pre->regen = data[27] & 0x07;
+	l1_pre->post_ext = data[28] & 0x01;
+	l1_pre->num_rf_freqs = data[29] & 0x07;
+	l1_pre->rf_idx = data[30] & 0x07;
+	version = (data[31] & 0x03) << 2;
+	version |= (data[32] & 0xc0) >> 6;
+	l1_pre->t2_version = (enum cxd2880_dvbt2_version)version;
+	l1_pre->l1_post_scrambled = (data[32] & 0x20) >> 5;
+	l1_pre->t2_base_lite = (data[32] & 0x10) >> 4;
+	l1_pre->crc32 = (data[33] << 24);
+	l1_pre->crc32 |= (data[34] << 16);
+	l1_pre->crc32 |= (data[35] << 8);
+	l1_pre->crc32 |= data[36];
+
+	if (profile == CXD2880_DVBT2_PROFILE_BASE) {
+		switch ((l1_pre->s2 >> 1)) {
+		case CXD2880_DVBT2_BASE_S2_M1K_G_ANY:
+			l1_pre->fft_mode = CXD2880_DVBT2_M1K;
+			break;
+		case CXD2880_DVBT2_BASE_S2_M2K_G_ANY:
+			l1_pre->fft_mode = CXD2880_DVBT2_M2K;
+			break;
+		case CXD2880_DVBT2_BASE_S2_M4K_G_ANY:
+			l1_pre->fft_mode = CXD2880_DVBT2_M4K;
+			break;
+		case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT:
+		case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2:
+			l1_pre->fft_mode = CXD2880_DVBT2_M8K;
+			break;
+		case CXD2880_DVBT2_BASE_S2_M16K_G_ANY:
+			l1_pre->fft_mode = CXD2880_DVBT2_M16K;
+			break;
+		case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT:
+		case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2:
+			l1_pre->fft_mode = CXD2880_DVBT2_M32K;
+			break;
+		default:
+			return -EAGAIN;
+		}
+	} else if (profile == CXD2880_DVBT2_PROFILE_LITE) {
+		switch ((l1_pre->s2 >> 1)) {
+		case CXD2880_DVBT2_LITE_S2_M2K_G_ANY:
+			l1_pre->fft_mode = CXD2880_DVBT2_M2K;
+			break;
+		case CXD2880_DVBT2_LITE_S2_M4K_G_ANY:
+			l1_pre->fft_mode = CXD2880_DVBT2_M4K;
+			break;
+		case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT:
+		case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2:
+			l1_pre->fft_mode = CXD2880_DVBT2_M8K;
+			break;
+		case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT:
+		case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2:
+			l1_pre->fft_mode = CXD2880_DVBT2_M16K;
+			break;
+		default:
+			return -EAGAIN;
+		}
+	} else {
+		return -EAGAIN;
+	}
+
+	l1_pre->mixed = l1_pre->s2 & 0x01;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     enum cxd2880_dvbt2_version
+				     *ver)
+{
+	u8 data[2];
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 version = 0;
+	int ret;
+
+	if (!tnr_dmd || !ver)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+					       &ts_lock,
+					       &unlock_detected);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (sync_state < 5) {
+		if (tnr_dmd->diver_mode ==
+		    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret =
+			    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+			    (tnr_dmd, &sync_state, &unlock_detected);
+			if (ret) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return ret;
+			}
+
+			if (sync_state < 5) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return -EAGAIN;
+			}
+		} else {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EAGAIN;
+		}
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x80, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	version = ((data[0] & 0x03) << 2);
+	version |= ((data[1] & 0xc0) >> 6);
+	*ver = (enum cxd2880_dvbt2_version)version;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd,
+				  struct cxd2880_dvbt2_ofdm *ofdm)
+{
+	u8 data[5];
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	int ret;
+
+	if (!tnr_dmd || !ofdm)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+					       &ts_lock,
+					       &unlock_detected);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (sync_state != 6) {
+		slvt_unfreeze_reg(tnr_dmd);
+
+		ret = -EAGAIN;
+
+		if (tnr_dmd->diver_mode ==
+		    CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret =
+			    cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd->diver_sub,
+							  ofdm);
+
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x1d, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	ofdm->mixed = ((data[0] & 0x20) ? 1 : 0);
+	ofdm->is_miso = ((data[0] & 0x10) >> 4);
+	ofdm->mode = (enum cxd2880_dvbt2_mode)(data[0] & 0x07);
+	ofdm->gi = (enum cxd2880_dvbt2_guard)((data[1] & 0x70) >> 4);
+	ofdm->pp = (enum cxd2880_dvbt2_pp)(data[1] & 0x07);
+	ofdm->bw_ext = (data[2] & 0x10) >> 4;
+	ofdm->papr = (enum cxd2880_dvbt2_papr)(data[2] & 0x0f);
+	ofdm->num_symbols = (data[3] << 8) | data[4];
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd
+				       *tnr_dmd, u8 *plp_ids,
+				       u8 *num_plps)
+{
+	u8 l1_post_ok = 0;
+	int ret;
+
+	if (!tnr_dmd || !num_plps)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret)
+		return ret;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x86, &l1_post_ok, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (!(l1_post_ok & 0x01)) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xc1, num_plps, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (*num_plps == 0) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EINVAL;
+	}
+
+	if (!plp_ids) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return 0;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xc2,
+				     plp_ids,
+				     ((*num_plps > 62) ?
+				     62 : *num_plps));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (*num_plps > 62) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x0c);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x10, plp_ids + 62,
+					     *num_plps - 62);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd
+					*tnr_dmd,
+					enum
+					cxd2880_dvbt2_plp_btype
+					type,
+					struct cxd2880_dvbt2_plp
+					*plp_info)
+{
+	u8 data[20];
+	u8 addr = 0;
+	u8 index = 0;
+	u8 l1_post_ok = 0;
+	int ret;
+
+	if (!tnr_dmd || !plp_info)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x86, &l1_post_ok, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (!l1_post_ok) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON)
+		addr = 0xa9;
+	else
+		addr = 0x96;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     addr, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	if (type == CXD2880_DVBT2_PLP_COMMON && !data[13])
+		return -EAGAIN;
+
+	plp_info->id = data[index++];
+	plp_info->type =
+	    (enum cxd2880_dvbt2_plp_type)(data[index++] & 0x07);
+	plp_info->payload =
+	    (enum cxd2880_dvbt2_plp_payload)(data[index++] & 0x1f);
+	plp_info->ff = data[index++] & 0x01;
+	plp_info->first_rf_idx = data[index++] & 0x07;
+	plp_info->first_frm_idx = data[index++];
+	plp_info->group_id = data[index++];
+	plp_info->plp_cr =
+	    (enum cxd2880_dvbt2_plp_code_rate)(data[index++] & 0x07);
+	plp_info->constell =
+	    (enum cxd2880_dvbt2_plp_constell)(data[index++] & 0x07);
+	plp_info->rot = data[index++] & 0x01;
+	plp_info->fec =
+	    (enum cxd2880_dvbt2_plp_fec)(data[index++] & 0x03);
+	plp_info->num_blocks_max = (data[index++] & 0x03) << 8;
+	plp_info->num_blocks_max |= data[index++];
+	plp_info->frm_int = data[index++];
+	plp_info->til_len = data[index++];
+	plp_info->til_type = data[index++] & 0x01;
+
+	plp_info->in_band_a_flag = data[index++] & 0x01;
+	plp_info->rsvd = data[index++] << 8;
+	plp_info->rsvd |= data[index++];
+
+	plp_info->in_band_b_flag =
+	    (plp_info->rsvd & 0x8000) >> 15;
+	plp_info->plp_mode =
+	    (enum cxd2880_dvbt2_plp_mode)((plp_info->rsvd & 0x000c) >> 2);
+	plp_info->static_flag = (plp_info->rsvd & 0x0002) >> 1;
+	plp_info->static_padding_flag = plp_info->rsvd & 0x0001;
+	plp_info->rsvd = (plp_info->rsvd & 0x7ff0) >> 4;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd
+					    *tnr_dmd,
+					    u8 *plp_error)
+{
+	u8 data;
+	int ret;
+
+	if (!tnr_dmd || !plp_error)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x86, &data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if ((data & 0x01) == 0x00) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xc0, &data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*plp_error = data & 0x01;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd
+				       *tnr_dmd, u8 *l1_change)
+{
+	u8 data;
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	int ret;
+
+	if (!tnr_dmd || !l1_change)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+					       &ts_lock,
+					       &unlock_detected);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (sync_state < 5) {
+		if (tnr_dmd->diver_mode ==
+		    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret =
+			    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+			    (tnr_dmd, &sync_state, &unlock_detected);
+			if (ret) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return ret;
+			}
+
+			if (sync_state < 5) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return -EAGAIN;
+			}
+		} else {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EAGAIN;
+		}
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x5f, &data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	*l1_change = data & 0x01;
+	if (*l1_change) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x22);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x16, 0x01);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+	}
+	slvt_unfreeze_reg(tnr_dmd);
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     struct cxd2880_dvbt2_l1post
+				     *l1_post)
+{
+	u8 data[16];
+	int ret;
+
+	if (!tnr_dmd || !l1_post)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x86, data, sizeof(data));
+	if (ret)
+		return ret;
+
+	if (!(data[0] & 0x01))
+		return -EAGAIN;
+
+	l1_post->sub_slices_per_frame = (data[1] & 0x7f) << 8;
+	l1_post->sub_slices_per_frame |= data[2];
+	l1_post->num_plps = data[3];
+	l1_post->num_aux = data[4] & 0x0f;
+	l1_post->aux_cfg_rfu = data[5];
+	l1_post->rf_idx = data[6] & 0x07;
+	l1_post->freq = data[7] << 24;
+	l1_post->freq |= data[8] << 16;
+	l1_post->freq |= data[9] << 8;
+	l1_post->freq |= data[10];
+	l1_post->fef_type = data[11] & 0x0f;
+	l1_post->fef_length = data[12] << 16;
+	l1_post->fef_length |= data[13] << 8;
+	l1_post->fef_length |= data[14];
+	l1_post->fef_intvl = data[15];
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd
+				      *tnr_dmd,
+				      enum cxd2880_dvbt2_plp_btype
+				      type,
+				      struct cxd2880_dvbt2_bbheader
+				      *bbheader)
+{
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 data[14];
+	u8 addr = 0;
+	int ret;
+
+	if (!tnr_dmd || !bbheader)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (!ts_lock) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON) {
+		u8 l1_post_ok;
+		u8 data;
+
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x86, &l1_post_ok, 1);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		if (!(l1_post_ok & 0x01)) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EAGAIN;
+		}
+
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0xb6, &data, 1);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		if (data == 0) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EAGAIN;
+		}
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON)
+		addr = 0x51;
+	else
+		addr = 0x42;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     addr, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	bbheader->stream_input =
+	    (enum cxd2880_dvbt2_stream)((data[0] >> 6) & 0x03);
+	bbheader->is_single_input_stream = (data[0] >> 5) & 0x01;
+	bbheader->is_constant_coding_modulation =
+	    (data[0] >> 4) & 0x01;
+	bbheader->issy_indicator = (data[0] >> 3) & 0x01;
+	bbheader->null_packet_deletion = (data[0] >> 2) & 0x01;
+	bbheader->ext = data[0] & 0x03;
+
+	bbheader->input_stream_identifier = data[1];
+	bbheader->plp_mode =
+	    (data[3] & 0x01) ? CXD2880_DVBT2_PLP_MODE_HEM :
+	    CXD2880_DVBT2_PLP_MODE_NM;
+	bbheader->data_field_length = (data[4] << 8) | data[5];
+
+	if (bbheader->plp_mode == CXD2880_DVBT2_PLP_MODE_NM) {
+		bbheader->user_packet_length =
+		    (data[6] << 8) | data[7];
+		bbheader->sync_byte = data[8];
+		bbheader->issy = 0;
+	} else {
+		bbheader->user_packet_length = 0;
+		bbheader->sync_byte = 0;
+		bbheader->issy =
+		    (data[11] << 16) | (data[12] << 8) | data[13];
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd
+					      *tnr_dmd,
+					      enum
+					      cxd2880_dvbt2_plp_btype
+					      type,
+					      u32 *ts_rate_bps)
+{
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 l1_post_ok = 0;
+	u8 data[4];
+	u8 addr = 0;
+
+	int ret;
+
+	if (!tnr_dmd || !ts_rate_bps)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+					       &ts_lock,
+					       &unlock_detected);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (!ts_lock) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x86, &l1_post_ok, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (!(l1_post_ok & 0x01)) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON)
+		addr = 0xba;
+	else
+		addr = 0xa7;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     addr, &data[0], 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if ((data[0] & 0x80) == 0x00) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x25);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON)
+		addr = 0xa6;
+	else
+		addr = 0xaa;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     addr, &data[0], 4);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	*ts_rate_bps = ((data[0] & 0x07) << 24) | (data[1] << 16) |
+		       (data[2] << 8) | data[3];
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd
+					    *tnr_dmd,
+					    enum
+					    cxd2880_tnrdmd_spectrum_sense
+					    *sense)
+{
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 early_unlock = 0;
+	u8 data = 0;
+	int ret;
+
+	if (!tnr_dmd || !sense)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, &ts_lock,
+					       &early_unlock);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (sync_state != 6) {
+		slvt_unfreeze_reg(tnr_dmd);
+
+		ret = -EAGAIN;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret =
+			    cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(tnr_dmd->diver_sub,
+								    sense);
+
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x2f, &data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*sense =
+	    (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV :
+	    CXD2880_TNRDMD_SPECTRUM_NORMAL;
+
+	return 0;
+}
+
+static int dvbt2_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd,
+			      u16 *reg_value)
+{
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 data[2];
+	int ret;
+
+	if (!tnr_dmd || !reg_value)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+					       &ts_lock,
+					       &unlock_detected);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (sync_state != 6) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x13, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*reg_value = (data[0] << 8) | data[1];
+
+	return ret;
+}
+
+static int dvbt2_calc_snr(struct cxd2880_tnrdmd *tnr_dmd,
+			  u32 reg_value, int *snr)
+{
+	if (!tnr_dmd || !snr)
+		return -EINVAL;
+
+	if (reg_value == 0)
+		return -EAGAIN;
+
+	if (reg_value > 10876)
+		reg_value = 10876;
+
+	*snr = intlog10(reg_value) - intlog10(12600 - reg_value);
+	*snr = (*snr + 839) / 1678 + 32000;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+				 int *snr)
+{
+	u16 reg_value = 0;
+	int ret;
+
+	if (!tnr_dmd || !snr)
+		return -EINVAL;
+
+	*snr = -1000 * 1000;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		ret = dvbt2_read_snr_reg(tnr_dmd, &reg_value);
+		if (ret)
+			return ret;
+
+		ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr);
+	} else {
+		int snr_main = 0;
+		int snr_sub = 0;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_snr_diver(tnr_dmd, snr, &snr_main,
+						       &snr_sub);
+	}
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd
+				       *tnr_dmd, int *snr,
+				       int *snr_main, int *snr_sub)
+{
+	u16 reg_value = 0;
+	u32 reg_value_sum = 0;
+	int ret;
+
+	if (!tnr_dmd || !snr || !snr_main || !snr_sub)
+		return -EINVAL;
+
+	*snr = -1000 * 1000;
+	*snr_main = -1000 * 1000;
+	*snr_sub = -1000 * 1000;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = dvbt2_read_snr_reg(tnr_dmd, &reg_value);
+	if (!ret) {
+		ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr_main);
+		if (ret)
+			reg_value = 0;
+	} else if (ret == -EAGAIN) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	ret = dvbt2_read_snr_reg(tnr_dmd->diver_sub, &reg_value);
+	if (!ret) {
+		ret = dvbt2_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub);
+		if (ret)
+			reg_value = 0;
+	} else if (ret == -EAGAIN) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	return dvbt2_calc_snr(tnr_dmd, reg_value_sum, snr);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct
+						 cxd2880_tnrdmd
+						 *tnr_dmd,
+						 u32 *pen)
+{
+	int ret;
+	u8 data[3];
+
+	if (!tnr_dmd || !pen)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x39, data, sizeof(data));
+	if (ret)
+		return ret;
+
+	if (!(data[0] & 0x01))
+		return -EAGAIN;
+
+	*pen = ((data[1] << 8) | data[2]);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd
+					     *tnr_dmd, int *ppm)
+{
+	u8 ctl_val_reg[5];
+	u8 nominal_rate_reg[5];
+	u32 trl_ctl_val = 0;
+	u32 trcg_nominal_rate = 0;
+	int num;
+	int den;
+	int ret;
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	s8 diff_upper = 0;
+
+	if (!tnr_dmd || !ppm)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+					       &ts_lock,
+					       &unlock_detected);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (sync_state != 6) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x34, ctl_val_reg,
+				     sizeof(ctl_val_reg));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x10, nominal_rate_reg,
+				     sizeof(nominal_rate_reg));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	diff_upper =
+	    (ctl_val_reg[0] & 0x7f) - (nominal_rate_reg[0] & 0x7f);
+
+	if (diff_upper < -1 || diff_upper > 1)
+		return -EAGAIN;
+
+	trl_ctl_val = ctl_val_reg[1] << 24;
+	trl_ctl_val |= ctl_val_reg[2] << 16;
+	trl_ctl_val |= ctl_val_reg[3] << 8;
+	trl_ctl_val |= ctl_val_reg[4];
+
+	trcg_nominal_rate = nominal_rate_reg[1] << 24;
+	trcg_nominal_rate |= nominal_rate_reg[2] << 16;
+	trcg_nominal_rate |= nominal_rate_reg[3] << 8;
+	trcg_nominal_rate |= nominal_rate_reg[4];
+
+	trl_ctl_val >>= 1;
+	trcg_nominal_rate >>= 1;
+
+	if (diff_upper == 1)
+		num =
+		    (int)((trl_ctl_val + 0x80000000u) -
+			  trcg_nominal_rate);
+	else if (diff_upper == -1)
+		num =
+		    -(int)((trcg_nominal_rate + 0x80000000u) -
+			   trl_ctl_val);
+	else
+		num = (int)(trl_ctl_val - trcg_nominal_rate);
+
+	den = (nominal_rate_reg[0] & 0x7f) << 24;
+	den |= nominal_rate_reg[1] << 16;
+	den |= nominal_rate_reg[2] << 8;
+	den |= nominal_rate_reg[3];
+	den = (den + (390625 / 2)) / 390625;
+
+	den >>= 1;
+
+	if (num >= 0)
+		*ppm = (num + (den / 2)) / den;
+	else
+		*ppm = (num - (den / 2)) / den;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct
+						 cxd2880_tnrdmd
+						 *tnr_dmd,
+						 int *ppm)
+{
+	if (!tnr_dmd || !ppm)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_dvbt2_mon_sampling_offset(tnr_dmd->diver_sub,
+							ppm);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd,
+				 enum cxd2880_dvbt2_plp_btype type,
+				 enum cxd2880_dvbt2_plp_constell *qam)
+{
+	u8 data;
+	u8 l1_post_ok = 0;
+	int ret;
+
+	if (!tnr_dmd || !qam)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x86, &l1_post_ok, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (!(l1_post_ok & 0x01)) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON) {
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0xb6, &data, 1);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		if (data == 0) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EAGAIN;
+		}
+
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0xb1, &data, 1);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+	} else {
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x9e, &data, 1);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*qam = (enum cxd2880_dvbt2_plp_constell)(data & 0x07);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd
+				       *tnr_dmd,
+				       enum cxd2880_dvbt2_plp_btype
+				       type,
+				       enum
+				       cxd2880_dvbt2_plp_code_rate
+				       *code_rate)
+{
+	u8 data;
+	u8 l1_post_ok = 0;
+	int ret;
+
+	if (!tnr_dmd || !code_rate)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x86, &l1_post_ok, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (!(l1_post_ok & 0x01)) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EAGAIN;
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON) {
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0xb6, &data, 1);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		if (data == 0) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EAGAIN;
+		}
+
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0xb0, &data, 1);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+	} else {
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x9d, &data, 1);
+		if (ret) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*code_rate = (enum cxd2880_dvbt2_plp_code_rate)(data & 0x07);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     enum cxd2880_dvbt2_profile
+				     *profile)
+{
+	u8 data;
+	int ret;
+
+	if (!tnr_dmd || !profile)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x22, &data, sizeof(data));
+	if (ret)
+		return ret;
+
+	if (data & 0x02) {
+		if (data & 0x01)
+			*profile = CXD2880_DVBT2_PROFILE_LITE;
+		else
+			*profile = CXD2880_DVBT2_PROFILE_BASE;
+	} else {
+		ret = -EAGAIN;
+		if (tnr_dmd->diver_mode ==
+		    CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret =
+			    cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd->diver_sub,
+							     profile);
+
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dvbt2_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+			  int rf_lvl, u8 *ssi)
+{
+	enum cxd2880_dvbt2_plp_constell qam;
+	enum cxd2880_dvbt2_plp_code_rate code_rate;
+	int prel;
+	int temp_ssi = 0;
+	int ret;
+
+	if (!tnr_dmd || !ssi)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_qam(tnr_dmd, CXD2880_DVBT2_PLP_DATA, &qam);
+	if (ret)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_code_rate(tnr_dmd, CXD2880_DVBT2_PLP_DATA,
+					       &code_rate);
+	if (ret)
+		return ret;
+
+	if (code_rate > CXD2880_DVBT2_R2_5 || qam > CXD2880_DVBT2_QAM256)
+		return -EINVAL;
+
+	prel = rf_lvl - ref_dbm_1000[qam][code_rate];
+
+	if (prel < -15000)
+		temp_ssi = 0;
+	else if (prel < 0)
+		temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000;
+	else if (prel < 20000)
+		temp_ssi = (((4 * prel) + 500) / 1000) + 10;
+	else if (prel < 35000)
+		temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90;
+	else
+		temp_ssi = 100;
+
+	*ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+				 u8 *ssi)
+{
+	int rf_lvl = 0;
+	int ret;
+
+	if (!tnr_dmd || !ssi)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+	if (ret)
+		return ret;
+
+	return dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd
+				     *tnr_dmd, u8 *ssi)
+{
+	int rf_lvl = 0;
+	int ret;
+
+	if (!tnr_dmd || !ssi)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+	if (ret)
+		return ret;
+
+	return dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi);
+}

+ 135 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h

@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT2_MON_H
+#define CXD2880_TNRDMD_DVBT2_MON_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_dvbt2.h"
+
+int cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd
+				       *tnr_dmd, u8 *sync_stat,
+				       u8 *ts_lock_stat,
+				       u8 *unlock_detected);
+
+int cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd
+					   *tnr_dmd,
+					   u8 *sync_stat,
+					   u8 *unlock_detected);
+
+int cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd
+					    *tnr_dmd, int *offset);
+
+int cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct
+						cxd2880_tnrdmd
+						*tnr_dmd,
+						int *offset);
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd,
+				    struct cxd2880_dvbt2_l1pre
+				    *l1_pre);
+
+int cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     enum cxd2880_dvbt2_version
+				     *ver);
+
+int cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd,
+				  struct cxd2880_dvbt2_ofdm *ofdm);
+
+int cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd
+				       *tnr_dmd, u8 *plp_ids,
+				       u8 *num_plps);
+
+int cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd
+					*tnr_dmd,
+					enum
+					cxd2880_dvbt2_plp_btype
+					type,
+					struct cxd2880_dvbt2_plp
+					*plp_info);
+
+int cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd
+					    *tnr_dmd,
+					    u8 *plp_error);
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd
+				       *tnr_dmd, u8 *l1_change);
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     struct cxd2880_dvbt2_l1post
+				     *l1_post);
+
+int cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd
+				      *tnr_dmd,
+				      enum cxd2880_dvbt2_plp_btype
+				      type,
+				      struct cxd2880_dvbt2_bbheader
+				      *bbheader);
+
+int cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd
+					      *tnr_dmd,
+					      enum
+					      cxd2880_dvbt2_plp_btype
+					      type,
+					      u32 *ts_rate_bps);
+
+int cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd
+					    *tnr_dmd,
+					    enum
+					    cxd2880_tnrdmd_spectrum_sense
+					    *sense);
+
+int cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+				 int *snr);
+
+int cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd
+				       *tnr_dmd, int *snr,
+				       int *snr_main,
+				       int *snr_sub);
+
+int cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct
+						 cxd2880_tnrdmd
+						 *tnr_dmd,
+						 u32 *pen);
+
+int cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd
+					     *tnr_dmd, int *ppm);
+
+int cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct
+						 cxd2880_tnrdmd
+						 *tnr_dmd,
+						 int *ppm);
+
+int cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd,
+				 enum cxd2880_dvbt2_plp_btype type,
+				 enum cxd2880_dvbt2_plp_constell
+				 *qam);
+
+int cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd
+				       *tnr_dmd,
+				       enum cxd2880_dvbt2_plp_btype
+				       type,
+				       enum
+				       cxd2880_dvbt2_plp_code_rate
+				       *code_rate);
+
+int cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     enum cxd2880_dvbt2_profile
+				     *profile);
+
+int cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+				 u8 *ssi);
+
+int cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd
+				     *tnr_dmd, u8 *ssi);
+
+#endif

+ 775 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c

@@ -0,0 +1,775 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+
+#include <media/dvb_math.h>
+
+static const int ref_dbm_1000[3][5] = {
+	{-93000, -91000, -90000, -89000, -88000},
+	{-87000, -85000, -84000, -83000, -82000},
+	{-82000, -80000, -78000, -77000, -76000},
+};
+
+static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd
+				      *tnr_dmd, u8 *sync_stat,
+				      u8 *ts_lock_stat,
+				      u8 *unlock_detected)
+{
+	u8 rdata = 0x00;
+	int ret;
+
+	if (!tnr_dmd || !sync_stat || !ts_lock_stat || !unlock_detected)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0d);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x10, &rdata, 1);
+	if (ret)
+		return ret;
+
+	*unlock_detected = (rdata & 0x10) ? 1 : 0;
+	*sync_stat = rdata & 0x07;
+	*ts_lock_stat = (rdata & 0x20) ? 1 : 0;
+
+	if (*sync_stat == 0x07)
+		return -EAGAIN;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd
+					  *tnr_dmd, u8 *sync_stat,
+					  u8 *unlock_detected)
+{
+	u8 ts_lock_stat = 0;
+
+	if (!tnr_dmd || !sync_stat || !unlock_detected)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd->diver_sub,
+						 sync_stat,
+						 &ts_lock_stat,
+						 unlock_detected);
+}
+
+int cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd
+				       *tnr_dmd,
+				       enum cxd2880_dvbt_mode
+				       *mode,
+				       enum cxd2880_dvbt_guard
+				       *guard)
+{
+	u8 rdata = 0x00;
+	int ret;
+
+	if (!tnr_dmd || !mode || !guard)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret =
+			    cxd2880_tnrdmd_dvbt_mon_mode_guard(tnr_dmd->diver_sub,
+							       mode, guard);
+
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0d);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x1b, &rdata, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*mode = (enum cxd2880_dvbt_mode)((rdata >> 2) & 0x03);
+	*guard = (enum cxd2880_dvbt_guard)(rdata & 0x03);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd
+					   *tnr_dmd, int *offset)
+{
+	u8 rdata[4];
+	u32 ctl_val = 0;
+	int ret;
+
+	if (!tnr_dmd || !offset)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0d);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x1d, rdata, 4);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	ctl_val =
+	    ((rdata[0] & 0x1f) << 24) | (rdata[1] << 16) | (rdata[2] << 8) |
+	    (rdata[3]);
+	*offset = cxd2880_convert2s_complement(ctl_val, 29);
+	*offset = -1 * ((*offset) * tnr_dmd->bandwidth / 235);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct
+					       cxd2880_tnrdmd
+					       *tnr_dmd,
+					       int *offset)
+{
+	if (!tnr_dmd || !offset)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_dvbt_mon_carrier_offset(tnr_dmd->diver_sub,
+						      offset);
+}
+
+int cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     struct cxd2880_dvbt_tpsinfo
+				     *info)
+{
+	u8 rdata[7];
+	u8 cell_id_ok = 0;
+	int ret;
+
+	if (!tnr_dmd || !info)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret =
+			    cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd->diver_sub,
+							     info);
+
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0d);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x29, rdata, 7);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x11);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xd5, &cell_id_ok, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	info->constellation =
+	    (enum cxd2880_dvbt_constellation)((rdata[0] >> 6) & 0x03);
+	info->hierarchy = (enum cxd2880_dvbt_hierarchy)((rdata[0] >> 3) & 0x07);
+	info->rate_hp = (enum cxd2880_dvbt_coderate)(rdata[0] & 0x07);
+	info->rate_lp = (enum cxd2880_dvbt_coderate)((rdata[1] >> 5) & 0x07);
+	info->guard = (enum cxd2880_dvbt_guard)((rdata[1] >> 3) & 0x03);
+	info->mode = (enum cxd2880_dvbt_mode)((rdata[1] >> 1) & 0x03);
+	info->fnum = (rdata[2] >> 6) & 0x03;
+	info->length_indicator = rdata[2] & 0x3f;
+	info->cell_id = (rdata[3] << 8) | rdata[4];
+	info->reserved_even = rdata[5] & 0x3f;
+	info->reserved_odd = rdata[6] & 0x3f;
+
+	info->cell_id_ok = cell_id_ok & 0x01;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct
+						cxd2880_tnrdmd
+						*tnr_dmd,
+						u32 *pen)
+{
+	u8 rdata[3];
+	int ret;
+
+	if (!tnr_dmd || !pen)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0d);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x26, rdata, 3);
+	if (ret)
+		return ret;
+
+	if (!(rdata[0] & 0x01))
+		return -EAGAIN;
+
+	*pen = (rdata[1] << 8) | rdata[2];
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd
+					   *tnr_dmd,
+					    enum
+					    cxd2880_tnrdmd_spectrum_sense
+					    *sense)
+{
+	u8 data = 0;
+	int ret;
+
+	if (!tnr_dmd || !sense)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(tnr_dmd->diver_sub,
+								     sense);
+
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0d);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x1c, &data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*sense =
+	    (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV :
+	    CXD2880_TNRDMD_SPECTRUM_NORMAL;
+
+	return ret;
+}
+
+static int dvbt_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd,
+			     u16 *reg_value)
+{
+	u8 rdata[2];
+	int ret;
+
+	if (!tnr_dmd || !reg_value)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0d);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x13, rdata, 2);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*reg_value = (rdata[0] << 8) | rdata[1];
+
+	return ret;
+}
+
+static int dvbt_calc_snr(struct cxd2880_tnrdmd *tnr_dmd,
+			 u32 reg_value, int *snr)
+{
+	if (!tnr_dmd || !snr)
+		return -EINVAL;
+
+	if (reg_value == 0)
+		return -EAGAIN;
+
+	if (reg_value > 4996)
+		reg_value = 4996;
+
+	*snr = intlog10(reg_value) - intlog10(5350 - reg_value);
+	*snr = (*snr + 839) / 1678 + 28500;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+				int *snr)
+{
+	u16 reg_value = 0;
+	int ret;
+
+	if (!tnr_dmd || !snr)
+		return -EINVAL;
+
+	*snr = -1000 * 1000;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		ret = dvbt_read_snr_reg(tnr_dmd, &reg_value);
+		if (ret)
+			return ret;
+
+		ret = dvbt_calc_snr(tnr_dmd, reg_value, snr);
+	} else {
+		int snr_main = 0;
+		int snr_sub = 0;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt_mon_snr_diver(tnr_dmd, snr, &snr_main,
+						      &snr_sub);
+	}
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd
+				      *tnr_dmd, int *snr,
+				      int *snr_main, int *snr_sub)
+{
+	u16 reg_value = 0;
+	u32 reg_value_sum = 0;
+	int ret;
+
+	if (!tnr_dmd || !snr || !snr_main || !snr_sub)
+		return -EINVAL;
+
+	*snr = -1000 * 1000;
+	*snr_main = -1000 * 1000;
+	*snr_sub = -1000 * 1000;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = dvbt_read_snr_reg(tnr_dmd, &reg_value);
+	if (!ret) {
+		ret = dvbt_calc_snr(tnr_dmd, reg_value, snr_main);
+		if (ret)
+			reg_value = 0;
+	} else if (ret == -EAGAIN) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	ret = dvbt_read_snr_reg(tnr_dmd->diver_sub, &reg_value);
+	if (!ret) {
+		ret = dvbt_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub);
+		if (ret)
+			reg_value = 0;
+	} else if (ret == -EAGAIN) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	return dvbt_calc_snr(tnr_dmd, reg_value_sum, snr);
+}
+
+int cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd
+					    *tnr_dmd, int *ppm)
+{
+	u8 ctl_val_reg[5];
+	u8 nominal_rate_reg[5];
+	u32 trl_ctl_val = 0;
+	u32 trcg_nominal_rate = 0;
+	int num;
+	int den;
+	s8 diff_upper = 0;
+	int ret;
+
+	if (!tnr_dmd || !ppm)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0d);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x21, ctl_val_reg,
+				     sizeof(ctl_val_reg));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x60, nominal_rate_reg,
+				     sizeof(nominal_rate_reg));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	diff_upper =
+	    (ctl_val_reg[0] & 0x7f) - (nominal_rate_reg[0] & 0x7f);
+
+	if (diff_upper < -1 || diff_upper > 1)
+		return -EAGAIN;
+
+	trl_ctl_val = ctl_val_reg[1] << 24;
+	trl_ctl_val |= ctl_val_reg[2] << 16;
+	trl_ctl_val |= ctl_val_reg[3] << 8;
+	trl_ctl_val |= ctl_val_reg[4];
+
+	trcg_nominal_rate = nominal_rate_reg[1] << 24;
+	trcg_nominal_rate |= nominal_rate_reg[2] << 16;
+	trcg_nominal_rate |= nominal_rate_reg[3] << 8;
+	trcg_nominal_rate |= nominal_rate_reg[4];
+
+	trl_ctl_val >>= 1;
+	trcg_nominal_rate >>= 1;
+
+	if (diff_upper == 1)
+		num =
+		    (int)((trl_ctl_val + 0x80000000u) -
+			  trcg_nominal_rate);
+	else if (diff_upper == -1)
+		num =
+		    -(int)((trcg_nominal_rate + 0x80000000u) -
+			   trl_ctl_val);
+	else
+		num = (int)(trl_ctl_val - trcg_nominal_rate);
+
+	den = (nominal_rate_reg[0] & 0x7f) << 24;
+	den |= nominal_rate_reg[1] << 16;
+	den |= nominal_rate_reg[2] << 8;
+	den |= nominal_rate_reg[3];
+	den = (den + (390625 / 2)) / 390625;
+
+	den >>= 1;
+
+	if (num >= 0)
+		*ppm = (num + (den / 2)) / den;
+	else
+		*ppm = (num - (den / 2)) / den;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct
+						cxd2880_tnrdmd
+						*tnr_dmd, int *ppm)
+{
+	if (!tnr_dmd || !ppm)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_dvbt_mon_sampling_offset(tnr_dmd->diver_sub, ppm);
+}
+
+static int dvbt_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+			 int rf_lvl, u8 *ssi)
+{
+	struct cxd2880_dvbt_tpsinfo tps;
+	int prel;
+	int temp_ssi = 0;
+	int ret;
+
+	if (!tnr_dmd || !ssi)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps);
+	if (ret)
+		return ret;
+
+	if (tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3 ||
+	    tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5)
+		return -EINVAL;
+
+	prel = rf_lvl - ref_dbm_1000[tps.constellation][tps.rate_hp];
+
+	if (prel < -15000)
+		temp_ssi = 0;
+	else if (prel < 0)
+		temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000;
+	else if (prel < 20000)
+		temp_ssi = (((4 * prel) + 500) / 1000) + 10;
+	else if (prel < 35000)
+		temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90;
+	else
+		temp_ssi = 100;
+
+	*ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+				u8 *ssi)
+{
+	int rf_lvl = 0;
+	int ret;
+
+	if (!tnr_dmd || !ssi)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+	if (ret)
+		return ret;
+
+	return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+}
+
+int cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				    u8 *ssi)
+{
+	int rf_lvl = 0;
+	int ret;
+
+	if (!tnr_dmd || !ssi)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+	if (ret)
+		return ret;
+
+	return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+}
+
+static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 sync = 0;
+	u8 tslock = 0;
+	u8 early_unlock = 0;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync, &tslock,
+					      &early_unlock);
+	if (ret)
+		return ret;
+
+	if (sync != 6)
+		return -EAGAIN;
+
+	return 0;
+}

+ 77 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h

@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT_MON_H
+#define CXD2880_TNRDMD_DVBT_MON_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_dvbt.h"
+
+int cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd
+				      *tnr_dmd, u8 *sync_stat,
+				      u8 *ts_lock_stat,
+				      u8 *unlock_detected);
+
+int cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd
+					  *tnr_dmd, u8 *sync_stat,
+					  u8 *unlock_detected);
+
+int cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd
+				       *tnr_dmd,
+				       enum cxd2880_dvbt_mode
+				       *mode,
+				       enum cxd2880_dvbt_guard
+				       *guard);
+
+int cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd
+					   *tnr_dmd, int *offset);
+
+int cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct
+					       cxd2880_tnrdmd
+					       *tnr_dmd,
+					       int *offset);
+
+int cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     struct cxd2880_dvbt_tpsinfo
+				     *info);
+
+int cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct
+						cxd2880_tnrdmd
+						*tnr_dmd,
+						u32 *pen);
+
+int cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd
+					   *tnr_dmd,
+					   enum
+					   cxd2880_tnrdmd_spectrum_sense
+					   *sense);
+
+int cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+				int *snr);
+
+int cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd
+				      *tnr_dmd, int *snr,
+				      int *snr_main, int *snr_sub);
+
+int cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd
+					    *tnr_dmd, int *ppm);
+
+int cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct
+						cxd2880_tnrdmd
+						*tnr_dmd,
+						int *ppm);
+
+int cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+				u8 *ssi);
+
+int cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				    u8 *ssi);
+
+#endif

+ 150 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c

@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd_mon.h"
+
+static const u8 rf_lvl_seq[2] = {
+	0x80, 0x00,
+};
+
+int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
+			      int *rf_lvl_db)
+{
+	u8 rdata[2];
+	int ret;
+
+	if (!tnr_dmd || !rf_lvl_db)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x10, 0x01);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x5b, rf_lvl_seq, 2);
+	if (ret)
+		return ret;
+
+	usleep_range(2000, 3000);
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x1a);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x15, rdata, 2);
+	if (ret)
+		return ret;
+
+	if (rdata[0] || rdata[1])
+		return -EINVAL;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x11, rdata, 2);
+	if (ret)
+		return ret;
+
+	*rf_lvl_db =
+	    cxd2880_convert2s_complement((rdata[0] << 3) |
+					 ((rdata[1] & 0xe0) >> 5), 11);
+
+	*rf_lvl_db *= 125;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x10, 0x00);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->rf_lvl_cmpstn)
+		ret = tnr_dmd->rf_lvl_cmpstn(tnr_dmd, rf_lvl_db);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				  int *rf_lvl_db)
+{
+	if (!tnr_dmd || !rf_lvl_db)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db);
+}
+
+int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
+					   *tnr_dmd, u16 *status)
+{
+	u8 data[2] = { 0 };
+	int ret;
+
+	if (!tnr_dmd || !status)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x00, 0x1a);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x15, data, 2);
+	if (ret)
+		return ret;
+
+	*status = (data[0] << 8) | data[1];
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
+					       cxd2880_tnrdmd
+					       *tnr_dmd,
+					       u16 *status)
+{
+	if (!tnr_dmd || !status)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	return cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub,
+						      status);
+}

+ 29 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h

@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_MON_H
+#define CXD2880_TNRDMD_MON_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
+			      int *rf_lvl_db);
+
+int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				  int *rf_lvl_db);
+
+int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
+					   *tnr_dmd, u16 *status);
+
+int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
+					       cxd2880_tnrdmd
+					       *tnr_dmd,
+					       u16 *status);
+#endif

+ 1947 - 0
drivers/media/dvb-frontends/cxd2880/cxd2880_top.c

@@ -0,0 +1,1947 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_top.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+#include <linux/spi/spi.h>
+
+#include <media/dvb_frontend.h>
+#include <media/dvb_math.h>
+
+#include "cxd2880.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+#include "cxd2880_integ.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_devio_spi.h"
+#include "cxd2880_spi_device.h"
+#include "cxd2880_tnrdmd_driver_version.h"
+
+struct cxd2880_priv {
+	struct cxd2880_tnrdmd tnrdmd;
+	struct spi_device *spi;
+	struct cxd2880_io regio;
+	struct cxd2880_spi_device spi_device;
+	struct cxd2880_spi cxd2880_spi;
+	struct cxd2880_dvbt_tune_param dvbt_tune_param;
+	struct cxd2880_dvbt2_tune_param dvbt2_tune_param;
+	struct mutex *spi_mutex; /* For SPI access exclusive control */
+	unsigned long pre_ber_update;
+	unsigned long pre_ber_interval;
+	unsigned long post_ber_update;
+	unsigned long post_ber_interval;
+	unsigned long ucblock_update;
+	unsigned long ucblock_interval;
+	enum fe_status s;
+};
+
+static int cxd2880_pre_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
+				 u32 *pre_bit_err, u32 *pre_bit_count)
+{
+	u8 rdata[2];
+	int ret;
+
+	if (!tnrdmd || !pre_bit_err || !pre_bit_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnrdmd);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x10);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x39, rdata, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	if ((rdata[0] & 0x01) == 0) {
+		slvt_unfreeze_reg(tnrdmd);
+		return -EAGAIN;
+	}
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x22, rdata, 2);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	*pre_bit_err = (rdata[0] << 8) | rdata[1];
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x6f, rdata, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnrdmd);
+
+	*pre_bit_count = ((rdata[0] & 0x07) == 0) ?
+			 256 : (0x1000 << (rdata[0] & 0x07));
+
+	return 0;
+}
+
+static int cxd2880_pre_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+				  u32 *pre_bit_err,
+				  u32 *pre_bit_count)
+{
+	u32 period_exp = 0;
+	u32 n_ldpc = 0;
+	u8 data[5];
+	int ret;
+
+	if (!tnrdmd || !pre_bit_err || !pre_bit_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnrdmd);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x3c, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	if (!(data[0] & 0x01)) {
+		slvt_unfreeze_reg(tnrdmd);
+		return -EAGAIN;
+	}
+	*pre_bit_err =
+	((data[1] & 0x0f) << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0xa0, data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
+	    CXD2880_DVBT2_FEC_LDPC_16K)
+		n_ldpc = 16200;
+	else
+		n_ldpc = 64800;
+	slvt_unfreeze_reg(tnrdmd);
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x20);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x6f, data, 1);
+	if (ret)
+		return ret;
+
+	period_exp = data[0] & 0x0f;
+
+	*pre_bit_count = (1U << period_exp) * n_ldpc;
+
+	return 0;
+}
+
+static int cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
+				  u32 *post_bit_err,
+				  u32 *post_bit_count)
+{
+	u8 rdata[3];
+	u32 bit_error = 0;
+	u32 period_exp = 0;
+	int ret;
+
+	if (!tnrdmd || !post_bit_err || !post_bit_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0d);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x15, rdata, 3);
+	if (ret)
+		return ret;
+
+	if ((rdata[0] & 0x40) == 0)
+		return -EAGAIN;
+
+	*post_bit_err = ((rdata[0] & 0x3f) << 16) | (rdata[1] << 8) | rdata[2];
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x10);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x60, rdata, 1);
+	if (ret)
+		return ret;
+
+	period_exp = (rdata[0] & 0x1f);
+
+	if (period_exp <= 11 && (bit_error > (1U << period_exp) * 204 * 8))
+		return -EAGAIN;
+
+	*post_bit_count = (1U << period_exp) * 204 * 8;
+
+	return 0;
+}
+
+static int cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+				   u32 *post_bit_err,
+				   u32 *post_bit_count)
+{
+	u32 period_exp = 0;
+	u32 n_bch = 0;
+	u8 data[3];
+	enum cxd2880_dvbt2_plp_fec plp_fec_type =
+		CXD2880_DVBT2_FEC_LDPC_16K;
+	enum cxd2880_dvbt2_plp_code_rate plp_code_rate =
+		CXD2880_DVBT2_R1_2;
+	int ret;
+	static const u16 n_bch_bits_lookup[2][8] = {
+		{7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
+		{32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
+	};
+
+	if (!tnrdmd || !post_bit_err || !post_bit_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnrdmd);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x15, data, 3);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	if (!(data[0] & 0x40)) {
+		slvt_unfreeze_reg(tnrdmd);
+		return -EAGAIN;
+	}
+
+	*post_bit_err =
+		((data[0] & 0x3f) << 16) | (data[1] << 8) | data[2];
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x9d, data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	plp_code_rate =
+	(enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0xa0, data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
+
+	slvt_unfreeze_reg(tnrdmd);
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x20);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x72, data, 1);
+	if (ret)
+		return ret;
+
+	period_exp = data[0] & 0x0f;
+
+	if (plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K ||
+	    plp_code_rate > CXD2880_DVBT2_R2_5)
+		return -EAGAIN;
+
+	n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
+
+	if (*post_bit_err > ((1U << period_exp) * n_bch))
+		return -EAGAIN;
+
+	*post_bit_count = (1U << period_exp) * n_bch;
+
+	return 0;
+}
+
+static int cxd2880_read_block_err_t(struct cxd2880_tnrdmd *tnrdmd,
+				    u32 *block_err,
+				    u32 *block_count)
+{
+	u8 rdata[3];
+	int ret;
+
+	if (!tnrdmd || !block_err || !block_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0d);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x18, rdata, 3);
+	if (ret)
+		return ret;
+
+	if ((rdata[0] & 0x01) == 0)
+		return -EAGAIN;
+
+	*block_err = (rdata[1] << 8) | rdata[2];
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x10);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x5c, rdata, 1);
+	if (ret)
+		return ret;
+
+	*block_count = 1U << (rdata[0] & 0x0f);
+
+	if ((*block_count == 0) || (*block_err > *block_count))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static int cxd2880_read_block_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+				     u32 *block_err,
+				     u32 *block_count)
+{
+	u8 rdata[3];
+	int ret;
+
+	if (!tnrdmd || !block_err || !block_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0b);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x18, rdata, 3);
+	if (ret)
+		return ret;
+
+	if ((rdata[0] & 0x01) == 0)
+		return -EAGAIN;
+
+	*block_err = (rdata[1] << 8) | rdata[2];
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x24);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0xdc, rdata, 1);
+	if (ret)
+		return ret;
+
+	*block_count = 1U << (rdata[0] & 0x0f);
+
+	if ((*block_count == 0) || (*block_err > *block_count))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static void cxd2880_release(struct dvb_frontend *fe)
+{
+	struct cxd2880_priv *priv = NULL;
+
+	if (!fe) {
+		pr_err("invalid arg.\n");
+		return;
+	}
+	priv = fe->demodulator_priv;
+	kfree(priv);
+}
+
+static int cxd2880_init(struct dvb_frontend *fe)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	struct cxd2880_tnrdmd_create_param create_param;
+
+	if (!fe) {
+		pr_err("invalid arg.\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI;
+	create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE;
+	create_param.en_internal_ldo = 1;
+	create_param.xosc_cap = 18;
+	create_param.xosc_i = 8;
+	create_param.stationary_use = 1;
+
+	mutex_lock(priv->spi_mutex);
+	if (priv->tnrdmd.io != &priv->regio) {
+		ret = cxd2880_tnrdmd_create(&priv->tnrdmd,
+					    &priv->regio, &create_param);
+		if (ret) {
+			mutex_unlock(priv->spi_mutex);
+			pr_info("cxd2880 tnrdmd create failed %d\n", ret);
+			return ret;
+		}
+	}
+	ret = cxd2880_integ_init(&priv->tnrdmd);
+	if (ret) {
+		mutex_unlock(priv->spi_mutex);
+		pr_err("cxd2880 integ init failed %d\n", ret);
+		return ret;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	pr_debug("OK.\n");
+
+	return ret;
+}
+
+static int cxd2880_sleep(struct dvb_frontend *fe)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd);
+	mutex_unlock(priv->spi_mutex);
+
+	pr_debug("tnrdmd_sleep ret %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_read_signal_strength(struct dvb_frontend *fe,
+					u16 *strength)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+	int level = 0;
+
+	if (!fe || !strength) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT ||
+	    c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level);
+	} else {
+		pr_debug("invalid system\n");
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	level /= 125;
+	/*
+	 * level should be between -105dBm and -30dBm.
+	 * E.g. they should be between:
+	 * -105000/125 = -840 and -30000/125 = -240
+	 */
+	level = clamp(level, -840, -240);
+	/* scale value to 0x0000-0xffff */
+	*strength = ((level + 840) * 0xffff) / (-240 + 840);
+
+	if (ret)
+		pr_debug("ret = %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	int ret;
+	int snrvalue = 0;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if (!fe || !snr) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd,
+						  &snrvalue);
+	} else if (c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd,
+						   &snrvalue);
+	} else {
+		pr_err("invalid system\n");
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	if (snrvalue < 0)
+		snrvalue = 0;
+	*snr = snrvalue;
+
+	if (ret)
+		pr_debug("ret = %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if (!fe || !ucblocks) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(&priv->tnrdmd,
+								  ucblocks);
+	} else if (c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(&priv->tnrdmd,
+								   ucblocks);
+	} else {
+		pr_err("invalid system\n");
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	if (ret)
+		pr_debug("ret = %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	*ber = 0;
+
+	return 0;
+}
+
+static int cxd2880_set_ber_per_period_t(struct dvb_frontend *fe)
+{
+	int ret;
+	struct cxd2880_priv *priv;
+	struct cxd2880_dvbt_tpsinfo info;
+	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+	u32 pre_ber_rate = 0;
+	u32 post_ber_rate = 0;
+	u32 ucblock_rate = 0;
+	u32 mes_exp = 0;
+	static const int cr_table[5] = {31500, 42000, 47250, 52500, 55125};
+	static const int denominator_tbl[4] = {125664, 129472, 137088, 152320};
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	bw = priv->dvbt_tune_param.bandwidth;
+
+	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd,
+					       &info);
+	if (ret) {
+		pr_err("tps monitor error ret = %d\n", ret);
+		info.hierarchy = CXD2880_DVBT_HIERARCHY_NON;
+		info.constellation = CXD2880_DVBT_CONSTELLATION_QPSK;
+		info.guard = CXD2880_DVBT_GUARD_1_4;
+		info.rate_hp = CXD2880_DVBT_CODERATE_1_2;
+		info.rate_lp = CXD2880_DVBT_CODERATE_1_2;
+	}
+
+	if (info.hierarchy == CXD2880_DVBT_HIERARCHY_NON) {
+		pre_ber_rate = 63000000 * bw * (info.constellation * 2 + 2) /
+			       denominator_tbl[info.guard];
+
+		post_ber_rate =	1000 * cr_table[info.rate_hp] * bw *
+				(info.constellation * 2 + 2) /
+				denominator_tbl[info.guard];
+
+		ucblock_rate = 875 * cr_table[info.rate_hp] * bw *
+			       (info.constellation * 2 + 2) /
+			       denominator_tbl[info.guard];
+	} else {
+		u8 data = 0;
+		struct cxd2880_tnrdmd *tnrdmd = &priv->tnrdmd;
+
+		ret = tnrdmd->io->write_reg(tnrdmd->io,
+					    CXD2880_IO_TGT_DMD,
+					    0x00, 0x10);
+		if (!ret) {
+			ret = tnrdmd->io->read_regs(tnrdmd->io,
+						    CXD2880_IO_TGT_DMD,
+						    0x67, &data, 1);
+			if (ret)
+				data = 0x00;
+		} else {
+			data = 0x00;
+		}
+
+		if (data & 0x01) { /* Low priority */
+			pre_ber_rate =
+				63000000 * bw * (info.constellation * 2 + 2) /
+				denominator_tbl[info.guard];
+
+			post_ber_rate = 1000 * cr_table[info.rate_lp] * bw *
+					(info.constellation * 2 + 2) /
+					denominator_tbl[info.guard];
+
+			ucblock_rate = (1000 * 7 / 8) *	cr_table[info.rate_lp] *
+				       bw * (info.constellation * 2 + 2) /
+				       denominator_tbl[info.guard];
+		} else { /* High priority */
+			pre_ber_rate =
+				63000000 * bw * 2 / denominator_tbl[info.guard];
+
+			post_ber_rate = 1000 * cr_table[info.rate_hp] * bw * 2 /
+					denominator_tbl[info.guard];
+
+			ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_hp] *
+					bw * 2 / denominator_tbl[info.guard];
+		}
+	}
+
+	mes_exp = pre_ber_rate < 8192 ? 8 : intlog2(pre_ber_rate) >> 24;
+	priv->pre_ber_interval =
+		((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) /
+		pre_ber_rate;
+	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+			       CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
+			       mes_exp == 8 ? 0 : mes_exp - 12);
+
+	mes_exp = intlog2(post_ber_rate) >> 24;
+	priv->post_ber_interval =
+		((1U << mes_exp) * 1000 + (post_ber_rate / 2)) /
+		post_ber_rate;
+	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+			       CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
+			       mes_exp);
+
+	mes_exp = intlog2(ucblock_rate) >> 24;
+	priv->ucblock_interval =
+		((1U << mes_exp) * 1000 + (ucblock_rate / 2)) /
+		ucblock_rate;
+	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+			       CXD2880_TNRDMD_CFG_DVBT_PER_MES,
+			       mes_exp);
+
+	return 0;
+}
+
+static int cxd2880_set_ber_per_period_t2(struct dvb_frontend *fe)
+{
+	int ret;
+	struct cxd2880_priv *priv;
+	struct cxd2880_dvbt2_l1pre l1pre;
+	struct cxd2880_dvbt2_l1post l1post;
+	struct cxd2880_dvbt2_plp plp;
+	struct cxd2880_dvbt2_bbheader bbheader;
+	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+	u32 pre_ber_rate = 0;
+	u32 post_ber_rate = 0;
+	u32 ucblock_rate = 0;
+	u32 mes_exp = 0;
+	u32 term_a = 0;
+	u32 term_b = 0;
+	u32 denominator = 0;
+	static const u32 gi_tbl[7] = {32, 64, 128, 256, 8, 152, 76};
+	static const u8 n_tbl[6] = {8, 2, 4, 16, 1, 1};
+	static const u8 mode_tbl[6] = {2, 8, 4, 1, 16, 32};
+	static const u32 kbch_tbl[2][8] = {
+		{6952, 9472, 10552, 11632, 12352, 13072, 5152, 6232},
+		{32128, 38608, 42960, 48328, 51568, 53760, 0, 0}
+	};
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	bw = priv->dvbt2_tune_param.bandwidth;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
+	if (ret) {
+		pr_info("l1 pre error\n");
+		goto error_ber_setting;
+	}
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_active_plp(&priv->tnrdmd,
+						  CXD2880_DVBT2_PLP_DATA, &plp);
+	if (ret) {
+		pr_info("plp info error\n");
+		goto error_ber_setting;
+	}
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_l1_post(&priv->tnrdmd, &l1post);
+	if (ret) {
+		pr_info("l1 post error\n");
+		goto error_ber_setting;
+	}
+
+	term_a =
+		(mode_tbl[l1pre.fft_mode] * (1024 + gi_tbl[l1pre.gi])) *
+		(l1pre.num_symbols + n_tbl[l1pre.fft_mode]) + 2048;
+
+	if (l1pre.mixed && l1post.fef_intvl) {
+		term_b = (l1post.fef_length + (l1post.fef_intvl / 2)) /
+			 l1post.fef_intvl;
+	} else {
+		term_b = 0;
+	}
+
+	switch (bw) {
+	case CXD2880_DTV_BW_1_7_MHZ:
+		denominator = ((term_a + term_b) * 71 + (131 / 2)) / 131;
+		break;
+	case CXD2880_DTV_BW_5_MHZ:
+		denominator = ((term_a + term_b) * 7 + 20) / 40;
+		break;
+	case CXD2880_DTV_BW_6_MHZ:
+		denominator = ((term_a + term_b) * 7 + 24) / 48;
+		break;
+	case CXD2880_DTV_BW_7_MHZ:
+		denominator = ((term_a + term_b) + 4) / 8;
+		break;
+	case CXD2880_DTV_BW_8_MHZ:
+	default:
+		denominator = ((term_a + term_b) * 7 + 32) / 64;
+		break;
+	}
+
+	if (plp.til_type && plp.til_len) {
+		pre_ber_rate =
+			(plp.num_blocks_max * 1000000 + (denominator / 2)) /
+			denominator;
+		pre_ber_rate = (pre_ber_rate + (plp.til_len / 2)) /
+			       plp.til_len;
+	} else {
+		pre_ber_rate =
+			(plp.num_blocks_max * 1000000 + (denominator / 2)) /
+			denominator;
+	}
+
+	post_ber_rate = pre_ber_rate;
+
+	mes_exp = intlog2(pre_ber_rate) >> 24;
+	priv->pre_ber_interval =
+		((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) /
+		pre_ber_rate;
+	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+			       CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
+			       mes_exp);
+
+	mes_exp = intlog2(post_ber_rate) >> 24;
+	priv->post_ber_interval =
+		((1U << mes_exp) * 1000 + (post_ber_rate / 2)) /
+		post_ber_rate;
+	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+			       CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
+			       mes_exp);
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_bbheader(&priv->tnrdmd,
+						CXD2880_DVBT2_PLP_DATA,
+						&bbheader);
+	if (ret) {
+		pr_info("bb header error\n");
+		goto error_ucblock_setting;
+	}
+
+	if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_NM) {
+		if (!bbheader.issy_indicator) {
+			ucblock_rate =
+				(pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] +
+				752) / 1504;
+		} else {
+			ucblock_rate =
+				(pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] +
+				764) / 1528;
+		}
+	} else if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_HEM) {
+		ucblock_rate =
+			(pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + 748) /
+			1496;
+	} else {
+		pr_info("plp mode is not Normal or HEM\n");
+		goto error_ucblock_setting;
+	}
+
+	mes_exp = intlog2(ucblock_rate) >> 24;
+	priv->ucblock_interval =
+		((1U << mes_exp) * 1000 + (ucblock_rate / 2)) /
+		ucblock_rate;
+	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+			       CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
+			       mes_exp);
+
+	return 0;
+
+error_ber_setting:
+	priv->pre_ber_interval = 1000;
+	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+				     CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, 0);
+
+	priv->post_ber_interval = 1000;
+	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+			       CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, 0);
+
+error_ucblock_setting:
+	priv->ucblock_interval = 1000;
+	cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+			       CXD2880_TNRDMD_CFG_DVBT2_PER_MES, 8);
+
+	return 0;
+}
+
+static int cxd2880_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd,
+			     struct cxd2880_dvbt_tune_param
+			     *tune_param)
+{
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	atomic_set(&tnr_dmd->cancel, 0);
+
+	if (tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) {
+		return -ENOTTY;
+	}
+
+	ret = cxd2880_tnrdmd_dvbt_tune1(tnr_dmd, tune_param);
+	if (ret)
+		return ret;
+
+	usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
+		     CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
+
+	return cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
+}
+
+static int cxd2880_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt2_tune_param
+			      *tune_param)
+{
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	atomic_set(&tnr_dmd->cancel, 0);
+
+	if (tune_param->bandwidth != CXD2880_DTV_BW_1_7_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) {
+		return -ENOTTY;
+	}
+
+	if (tune_param->profile != CXD2880_DVBT2_PROFILE_BASE &&
+	    tune_param->profile != CXD2880_DVBT2_PROFILE_LITE)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_dvbt2_tune1(tnr_dmd, tune_param);
+	if (ret)
+		return ret;
+
+	usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
+		     CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
+
+	return cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
+}
+
+static int cxd2880_set_frontend(struct dvb_frontend *fe)
+{
+	int ret;
+	struct dtv_frontend_properties *c;
+	struct cxd2880_priv *priv;
+	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->pre_bit_error.stat[0].uvalue = 0;
+	c->pre_bit_error.len = 1;
+	c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->pre_bit_count.stat[0].uvalue = 0;
+	c->pre_bit_count.len = 1;
+	c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->post_bit_error.stat[0].uvalue = 0;
+	c->post_bit_error.len = 1;
+	c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->post_bit_count.stat[0].uvalue = 0;
+	c->post_bit_count.len = 1;
+	c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->block_error.stat[0].uvalue = 0;
+	c->block_error.len = 1;
+	c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->block_count.stat[0].uvalue = 0;
+	c->block_count.len = 1;
+
+	switch (c->bandwidth_hz) {
+	case 1712000:
+		bw = CXD2880_DTV_BW_1_7_MHZ;
+		break;
+	case 5000000:
+		bw = CXD2880_DTV_BW_5_MHZ;
+		break;
+	case 6000000:
+		bw = CXD2880_DTV_BW_6_MHZ;
+		break;
+	case 7000000:
+		bw = CXD2880_DTV_BW_7_MHZ;
+		break;
+	case 8000000:
+		bw = CXD2880_DTV_BW_8_MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	priv->s = 0;
+
+	pr_info("sys:%d freq:%d bw:%d\n",
+		c->delivery_system, c->frequency, bw);
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT;
+		priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000;
+		priv->dvbt_tune_param.bandwidth = bw;
+		priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP;
+		ret = cxd2880_dvbt_tune(&priv->tnrdmd,
+					&priv->dvbt_tune_param);
+	} else if (c->delivery_system == SYS_DVBT2) {
+		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2;
+		priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000;
+		priv->dvbt2_tune_param.bandwidth = bw;
+		priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id;
+		priv->dvbt2_tune_param.profile = CXD2880_DVBT2_PROFILE_BASE;
+		ret = cxd2880_dvbt2_tune(&priv->tnrdmd,
+					 &priv->dvbt2_tune_param);
+	} else {
+		pr_err("invalid system\n");
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	pr_info("tune result %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_get_stats(struct dvb_frontend *fe,
+			     enum fe_status status)
+{
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+	u32 pre_bit_err = 0, pre_bit_count = 0;
+	u32 post_bit_err = 0, post_bit_count = 0;
+	u32 block_err = 0, block_count = 0;
+	int ret;
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	if (!(status & FE_HAS_LOCK)) {
+		c->pre_bit_error.len = 1;
+		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->pre_bit_count.len = 1;
+		c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->post_bit_error.len = 1;
+		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->post_bit_count.len = 1;
+		c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->block_error.len = 1;
+		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->block_count.len = 1;
+		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+		return 0;
+	}
+
+	if (time_after(jiffies, priv->pre_ber_update)) {
+		priv->pre_ber_update =
+			 jiffies + msecs_to_jiffies(priv->pre_ber_interval);
+		if (c->delivery_system == SYS_DVBT) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_pre_bit_err_t(&priv->tnrdmd,
+						    &pre_bit_err,
+						    &pre_bit_count);
+			mutex_unlock(priv->spi_mutex);
+		} else if (c->delivery_system == SYS_DVBT2) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd,
+						     &pre_bit_err,
+						     &pre_bit_count);
+			mutex_unlock(priv->spi_mutex);
+		} else {
+			return -EINVAL;
+		}
+
+		if (!ret) {
+			c->pre_bit_error.len = 1;
+			c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+			c->pre_bit_error.stat[0].uvalue += pre_bit_err;
+			c->pre_bit_count.len = 1;
+			c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+			c->pre_bit_count.stat[0].uvalue += pre_bit_count;
+		} else {
+			c->pre_bit_error.len = 1;
+			c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+			c->pre_bit_count.len = 1;
+			c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+			pr_debug("pre_bit_error_t failed %d\n", ret);
+		}
+	}
+
+	if (time_after(jiffies, priv->post_ber_update)) {
+		priv->post_ber_update =
+			jiffies + msecs_to_jiffies(priv->post_ber_interval);
+		if (c->delivery_system == SYS_DVBT) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_post_bit_err_t(&priv->tnrdmd,
+						     &post_bit_err,
+						     &post_bit_count);
+			mutex_unlock(priv->spi_mutex);
+		} else if (c->delivery_system == SYS_DVBT2) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_post_bit_err_t2(&priv->tnrdmd,
+						      &post_bit_err,
+						      &post_bit_count);
+			mutex_unlock(priv->spi_mutex);
+		} else {
+			return -EINVAL;
+		}
+
+		if (!ret) {
+			c->post_bit_error.len = 1;
+			c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+			c->post_bit_error.stat[0].uvalue += post_bit_err;
+			c->post_bit_count.len = 1;
+			c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+			c->post_bit_count.stat[0].uvalue += post_bit_count;
+		} else {
+			c->post_bit_error.len = 1;
+			c->post_bit_error.stat[0].scale =
+							FE_SCALE_NOT_AVAILABLE;
+			c->post_bit_count.len = 1;
+			c->post_bit_count.stat[0].scale =
+							FE_SCALE_NOT_AVAILABLE;
+			pr_debug("post_bit_err_t %d\n", ret);
+		}
+	}
+
+	if (time_after(jiffies, priv->ucblock_update)) {
+		priv->ucblock_update =
+			jiffies + msecs_to_jiffies(priv->ucblock_interval);
+		if (c->delivery_system == SYS_DVBT) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_read_block_err_t(&priv->tnrdmd,
+						       &block_err,
+						       &block_count);
+			mutex_unlock(priv->spi_mutex);
+		} else if (c->delivery_system == SYS_DVBT2) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_read_block_err_t2(&priv->tnrdmd,
+							&block_err,
+							&block_count);
+			mutex_unlock(priv->spi_mutex);
+		} else {
+			return -EINVAL;
+		}
+		if (!ret) {
+			c->block_error.len = 1;
+			c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+			c->block_error.stat[0].uvalue += block_err;
+			c->block_count.len = 1;
+			c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+			c->block_count.stat[0].uvalue += block_count;
+		} else {
+			c->block_error.len = 1;
+			c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+			c->block_count.len = 1;
+			c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+			pr_debug("read_block_err_t  %d\n", ret);
+		}
+	}
+
+	return 0;
+}
+
+static int cxd2880_check_l1post_plp(struct dvb_frontend *fe)
+{
+	u8 valid = 0;
+	u8 plp_not_found;
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	ret = cxd2880_tnrdmd_dvbt2_check_l1post_valid(&priv->tnrdmd,
+						      &valid);
+	if (ret)
+		return ret;
+
+	if (!valid)
+		return -EAGAIN;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_data_plp_error(&priv->tnrdmd,
+						      &plp_not_found);
+	if (ret)
+		return ret;
+
+	if (plp_not_found) {
+		priv->dvbt2_tune_param.tune_info =
+			CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID;
+	} else {
+		priv->dvbt2_tune_param.tune_info =
+			CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK;
+	}
+
+	return 0;
+}
+
+static int cxd2880_read_status(struct dvb_frontend *fe,
+			       enum fe_status *status)
+{
+	int ret;
+	u8 sync = 0;
+	u8 lock = 0;
+	u8 unlock = 0;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if (!fe || !status) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+	*status = 0;
+
+	if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) {
+		mutex_lock(priv->spi_mutex);
+		if (c->delivery_system == SYS_DVBT) {
+			ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(&priv->tnrdmd,
+								&sync,
+								&lock,
+								&unlock);
+		} else if (c->delivery_system == SYS_DVBT2) {
+			ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(&priv->tnrdmd,
+								 &sync,
+								 &lock,
+								 &unlock);
+		} else {
+			pr_err("invalid system");
+			mutex_unlock(priv->spi_mutex);
+			return -EINVAL;
+		}
+
+		mutex_unlock(priv->spi_mutex);
+		if (ret) {
+			pr_err("failed. sys = %d\n", priv->tnrdmd.sys);
+			return  ret;
+		}
+
+		if (sync == 6) {
+			*status = FE_HAS_SIGNAL |
+				  FE_HAS_CARRIER;
+		}
+		if (lock)
+			*status |= FE_HAS_VITERBI |
+				   FE_HAS_SYNC |
+				   FE_HAS_LOCK;
+	}
+
+	pr_debug("status %d\n", *status);
+
+	if (priv->s == 0 && (*status & FE_HAS_LOCK)) {
+		mutex_lock(priv->spi_mutex);
+		if (c->delivery_system == SYS_DVBT) {
+			ret = cxd2880_set_ber_per_period_t(fe);
+			priv->s = *status;
+		} else if (c->delivery_system == SYS_DVBT2) {
+			ret = cxd2880_check_l1post_plp(fe);
+			if (!ret) {
+				ret = cxd2880_set_ber_per_period_t2(fe);
+				priv->s = *status;
+			}
+		} else {
+			pr_err("invalid system\n");
+			mutex_unlock(priv->spi_mutex);
+			return -EINVAL;
+		}
+		mutex_unlock(priv->spi_mutex);
+	}
+
+	cxd2880_get_stats(fe, *status);
+	return  0;
+}
+
+static int cxd2880_tune(struct dvb_frontend *fe,
+			bool retune,
+			unsigned int mode_flags,
+			unsigned int *delay,
+			enum fe_status *status)
+{
+	int ret;
+
+	if (!fe || !delay || !status) {
+		pr_err("invalid arg.");
+		return -EINVAL;
+	}
+
+	if (retune) {
+		ret = cxd2880_set_frontend(fe);
+		if (ret) {
+			pr_err("cxd2880_set_frontend failed %d\n", ret);
+			return ret;
+		}
+	}
+
+	*delay = HZ / 5;
+
+	return cxd2880_read_status(fe, status);
+}
+
+static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
+				  struct dtv_frontend_properties *c)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
+	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
+	struct cxd2880_dvbt_tpsinfo tps;
+	enum cxd2880_tnrdmd_spectrum_sense sense;
+	u16 snr = 0;
+	int strength = 0;
+
+	if (!fe || !c) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
+						 &mode, &guard);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (mode) {
+		case CXD2880_DVBT_MODE_2K:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			break;
+		case CXD2880_DVBT_MODE_8K:
+			c->transmission_mode = TRANSMISSION_MODE_8K;
+			break;
+		default:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			pr_debug("transmission mode is invalid %d\n", mode);
+			break;
+		}
+		switch (guard) {
+		case CXD2880_DVBT_GUARD_1_32:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case CXD2880_DVBT_GUARD_1_16:
+			c->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case CXD2880_DVBT_GUARD_1_8:
+			c->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case CXD2880_DVBT_GUARD_1_4:
+			c->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		default:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			pr_debug("guard interval is invalid %d\n",
+				 guard);
+			break;
+		}
+	} else {
+		c->transmission_mode = TRANSMISSION_MODE_2K;
+		c->guard_interval = GUARD_INTERVAL_1_32;
+		pr_debug("ModeGuard err %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (tps.hierarchy) {
+		case CXD2880_DVBT_HIERARCHY_NON:
+			c->hierarchy = HIERARCHY_NONE;
+			break;
+		case CXD2880_DVBT_HIERARCHY_1:
+			c->hierarchy = HIERARCHY_1;
+			break;
+		case CXD2880_DVBT_HIERARCHY_2:
+			c->hierarchy = HIERARCHY_2;
+			break;
+		case CXD2880_DVBT_HIERARCHY_4:
+			c->hierarchy = HIERARCHY_4;
+			break;
+		default:
+			c->hierarchy = HIERARCHY_NONE;
+			pr_debug("TPSInfo hierarchy is invalid %d\n",
+				 tps.hierarchy);
+			break;
+		}
+
+		switch (tps.rate_hp) {
+		case CXD2880_DVBT_CODERATE_1_2:
+			c->code_rate_HP = FEC_1_2;
+			break;
+		case CXD2880_DVBT_CODERATE_2_3:
+			c->code_rate_HP = FEC_2_3;
+			break;
+		case CXD2880_DVBT_CODERATE_3_4:
+			c->code_rate_HP = FEC_3_4;
+			break;
+		case CXD2880_DVBT_CODERATE_5_6:
+			c->code_rate_HP = FEC_5_6;
+			break;
+		case CXD2880_DVBT_CODERATE_7_8:
+			c->code_rate_HP = FEC_7_8;
+			break;
+		default:
+			c->code_rate_HP = FEC_NONE;
+			pr_debug("TPSInfo rateHP is invalid %d\n",
+				 tps.rate_hp);
+			break;
+		}
+		switch (tps.rate_lp) {
+		case CXD2880_DVBT_CODERATE_1_2:
+			c->code_rate_LP = FEC_1_2;
+			break;
+		case CXD2880_DVBT_CODERATE_2_3:
+			c->code_rate_LP = FEC_2_3;
+			break;
+		case CXD2880_DVBT_CODERATE_3_4:
+			c->code_rate_LP = FEC_3_4;
+			break;
+		case CXD2880_DVBT_CODERATE_5_6:
+			c->code_rate_LP = FEC_5_6;
+			break;
+		case CXD2880_DVBT_CODERATE_7_8:
+			c->code_rate_LP = FEC_7_8;
+			break;
+		default:
+			c->code_rate_LP = FEC_NONE;
+			pr_debug("TPSInfo rateLP is invalid %d\n",
+				 tps.rate_lp);
+			break;
+		}
+		switch (tps.constellation) {
+		case CXD2880_DVBT_CONSTELLATION_QPSK:
+			c->modulation = QPSK;
+			break;
+		case CXD2880_DVBT_CONSTELLATION_16QAM:
+			c->modulation = QAM_16;
+			break;
+		case CXD2880_DVBT_CONSTELLATION_64QAM:
+			c->modulation = QAM_64;
+			break;
+		default:
+			c->modulation = QPSK;
+			pr_debug("TPSInfo constellation is invalid %d\n",
+				 tps.constellation);
+			break;
+		}
+	} else {
+		c->hierarchy = HIERARCHY_NONE;
+		c->code_rate_HP = FEC_NONE;
+		c->code_rate_LP = FEC_NONE;
+		c->modulation = QPSK;
+		pr_debug("TPS info err %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (sense) {
+		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+			c->inversion = INVERSION_OFF;
+			break;
+		case CXD2880_TNRDMD_SPECTRUM_INV:
+			c->inversion = INVERSION_ON;
+			break;
+		default:
+			c->inversion = INVERSION_OFF;
+			pr_debug("spectrum sense is invalid %d\n", sense);
+			break;
+		}
+	} else {
+		c->inversion = INVERSION_OFF;
+		pr_debug("spectrum_sense %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+		c->strength.stat[0].svalue = strength;
+	} else {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		pr_debug("mon_rf_lvl %d\n", ret);
+	}
+
+	ret = cxd2880_read_snr(fe, &snr);
+	if (!ret) {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = snr;
+	} else {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		pr_debug("read_snr %d\n", ret);
+	}
+
+	return 0;
+}
+
+static int cxd2880_get_frontend_t2(struct dvb_frontend *fe,
+				   struct dtv_frontend_properties *c)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	struct cxd2880_dvbt2_l1pre l1pre;
+	enum cxd2880_dvbt2_plp_code_rate coderate;
+	enum cxd2880_dvbt2_plp_constell qam;
+	enum cxd2880_tnrdmd_spectrum_sense sense;
+	u16 snr = 0;
+	int strength = 0;
+
+	if (!fe || !c) {
+		pr_err("invalid arg.\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (l1pre.fft_mode) {
+		case CXD2880_DVBT2_M2K:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			break;
+		case CXD2880_DVBT2_M8K:
+			c->transmission_mode = TRANSMISSION_MODE_8K;
+			break;
+		case CXD2880_DVBT2_M4K:
+			c->transmission_mode = TRANSMISSION_MODE_4K;
+			break;
+		case CXD2880_DVBT2_M1K:
+			c->transmission_mode = TRANSMISSION_MODE_1K;
+			break;
+		case CXD2880_DVBT2_M16K:
+			c->transmission_mode = TRANSMISSION_MODE_16K;
+			break;
+		case CXD2880_DVBT2_M32K:
+			c->transmission_mode = TRANSMISSION_MODE_32K;
+			break;
+		default:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			pr_debug("L1Pre fft_mode is invalid %d\n",
+				 l1pre.fft_mode);
+			break;
+		}
+		switch (l1pre.gi) {
+		case CXD2880_DVBT2_G1_32:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case CXD2880_DVBT2_G1_16:
+			c->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case CXD2880_DVBT2_G1_8:
+			c->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case CXD2880_DVBT2_G1_4:
+			c->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		case CXD2880_DVBT2_G1_128:
+			c->guard_interval = GUARD_INTERVAL_1_128;
+			break;
+		case CXD2880_DVBT2_G19_128:
+			c->guard_interval = GUARD_INTERVAL_19_128;
+			break;
+		case CXD2880_DVBT2_G19_256:
+			c->guard_interval = GUARD_INTERVAL_19_256;
+			break;
+		default:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			pr_debug("L1Pre guard interval is invalid %d\n",
+				 l1pre.gi);
+			break;
+		}
+	} else {
+		c->transmission_mode = TRANSMISSION_MODE_2K;
+		c->guard_interval = GUARD_INTERVAL_1_32;
+		pr_debug("L1Pre err %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd,
+						 CXD2880_DVBT2_PLP_DATA,
+						 &coderate);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (coderate) {
+		case CXD2880_DVBT2_R1_2:
+			c->fec_inner = FEC_1_2;
+			break;
+		case CXD2880_DVBT2_R3_5:
+			c->fec_inner = FEC_3_5;
+			break;
+		case CXD2880_DVBT2_R2_3:
+			c->fec_inner = FEC_2_3;
+			break;
+		case CXD2880_DVBT2_R3_4:
+			c->fec_inner = FEC_3_4;
+			break;
+		case CXD2880_DVBT2_R4_5:
+			c->fec_inner = FEC_4_5;
+			break;
+		case CXD2880_DVBT2_R5_6:
+			c->fec_inner = FEC_5_6;
+			break;
+		default:
+			c->fec_inner = FEC_NONE;
+			pr_debug("CodeRate is invalid %d\n", coderate);
+			break;
+		}
+	} else {
+		c->fec_inner = FEC_NONE;
+		pr_debug("CodeRate %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd,
+					   CXD2880_DVBT2_PLP_DATA,
+					   &qam);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (qam) {
+		case CXD2880_DVBT2_QPSK:
+			c->modulation = QPSK;
+			break;
+		case CXD2880_DVBT2_QAM16:
+			c->modulation = QAM_16;
+			break;
+		case CXD2880_DVBT2_QAM64:
+			c->modulation = QAM_64;
+			break;
+		case CXD2880_DVBT2_QAM256:
+			c->modulation = QAM_256;
+			break;
+		default:
+			c->modulation = QPSK;
+			pr_debug("QAM is invalid %d\n", qam);
+			break;
+		}
+	} else {
+		c->modulation = QPSK;
+		pr_debug("QAM %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (sense) {
+		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+			c->inversion = INVERSION_OFF;
+			break;
+		case CXD2880_TNRDMD_SPECTRUM_INV:
+			c->inversion = INVERSION_ON;
+			break;
+		default:
+			c->inversion = INVERSION_OFF;
+			pr_debug("spectrum sense is invalid %d\n", sense);
+			break;
+		}
+	} else {
+		c->inversion = INVERSION_OFF;
+		pr_debug("SpectrumSense %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+		c->strength.stat[0].svalue = strength;
+	} else {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		pr_debug("mon_rf_lvl %d\n", ret);
+	}
+
+	ret = cxd2880_read_snr(fe, &snr);
+	if (!ret) {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = snr;
+	} else {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		pr_debug("read_snr %d\n", ret);
+	}
+
+	return 0;
+}
+
+static int cxd2880_get_frontend(struct dvb_frontend *fe,
+				struct dtv_frontend_properties *props)
+{
+	int ret;
+
+	if (!fe || !props) {
+		pr_err("invalid arg.");
+		return -EINVAL;
+	}
+
+	pr_debug("system=%d\n", fe->dtv_property_cache.delivery_system);
+	switch (fe->dtv_property_cache.delivery_system) {
+	case SYS_DVBT:
+		ret = cxd2880_get_frontend_t(fe, props);
+		break;
+	case SYS_DVBT2:
+		ret = cxd2880_get_frontend_t2(fe, props);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe)
+{
+	return DVBFE_ALGO_HW;
+}
+
+static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = {
+	.info = {
+		.name = "Sony CXD2880",
+		.frequency_min =  174000000,
+		.frequency_max = 862000000,
+		.frequency_stepsize = 1000,
+		.caps = FE_CAN_INVERSION_AUTO |
+				FE_CAN_FEC_1_2 |
+				FE_CAN_FEC_2_3 |
+				FE_CAN_FEC_3_4 |
+				FE_CAN_FEC_4_5 |
+				FE_CAN_FEC_5_6	|
+				FE_CAN_FEC_7_8	|
+				FE_CAN_FEC_AUTO |
+				FE_CAN_QPSK |
+				FE_CAN_QAM_16 |
+				FE_CAN_QAM_32 |
+				FE_CAN_QAM_64 |
+				FE_CAN_QAM_128 |
+				FE_CAN_QAM_256 |
+				FE_CAN_QAM_AUTO |
+				FE_CAN_TRANSMISSION_MODE_AUTO |
+				FE_CAN_GUARD_INTERVAL_AUTO |
+				FE_CAN_2G_MODULATION |
+				FE_CAN_RECOVER |
+				FE_CAN_MUTE_TS,
+	},
+	.delsys = { SYS_DVBT, SYS_DVBT2 },
+
+	.release = cxd2880_release,
+	.init = cxd2880_init,
+	.sleep = cxd2880_sleep,
+	.tune = cxd2880_tune,
+	.set_frontend = cxd2880_set_frontend,
+	.get_frontend = cxd2880_get_frontend,
+	.read_status = cxd2880_read_status,
+	.read_ber = cxd2880_read_ber,
+	.read_signal_strength = cxd2880_read_signal_strength,
+	.read_snr = cxd2880_read_snr,
+	.read_ucblocks = cxd2880_read_ucblocks,
+	.get_frontend_algo = cxd2880_get_frontend_algo,
+};
+
+struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+				    struct cxd2880_config *cfg)
+{
+	int ret;
+	enum cxd2880_tnrdmd_chip_id chipid =
+					CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+	static struct cxd2880_priv *priv;
+	u8 data = 0;
+
+	if (!fe) {
+		pr_err("invalid arg.\n");
+		return NULL;
+	}
+
+	priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->spi = cfg->spi;
+	priv->spi_mutex = cfg->spi_mutex;
+	priv->spi_device.spi = cfg->spi;
+
+	memcpy(&fe->ops, &cxd2880_dvbt_t2_ops,
+	       sizeof(struct dvb_frontend_ops));
+
+	ret = cxd2880_spi_device_initialize(&priv->spi_device,
+					    CXD2880_SPI_MODE_0,
+					    55000000);
+	if (ret) {
+		pr_err("spi_device_initialize failed. %d\n", ret);
+		kfree(priv);
+		return NULL;
+	}
+
+	ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi,
+					    &priv->spi_device);
+	if (ret) {
+		pr_err("spi_device_create_spi failed. %d\n", ret);
+		kfree(priv);
+		return NULL;
+	}
+
+	ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0);
+	if (ret) {
+		pr_err("io_spi_create failed. %d\n", ret);
+		kfree(priv);
+		return NULL;
+	}
+	ret = priv->regio.write_reg(&priv->regio,
+				    CXD2880_IO_TGT_SYS, 0x00, 0x00);
+	if (ret) {
+		pr_err("set bank to 0x00 failed.\n");
+		kfree(priv);
+		return NULL;
+	}
+	ret = priv->regio.read_regs(&priv->regio,
+				    CXD2880_IO_TGT_SYS, 0xfd, &data, 1);
+	if (ret) {
+		pr_err("read chip id failed.\n");
+		kfree(priv);
+		return NULL;
+	}
+
+	chipid = (enum cxd2880_tnrdmd_chip_id)data;
+	if (chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X &&
+	    chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11) {
+		pr_err("chip id invalid.\n");
+		kfree(priv);
+		return NULL;
+	}
+
+	fe->demodulator_priv = priv;
+	pr_info("CXD2880 driver version: Ver %s\n",
+		CXD2880_TNRDMD_DRIVER_VERSION);
+
+	return fe;
+}
+EXPORT_SYMBOL(cxd2880_attach);
+
+MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");

+ 2 - 2
drivers/media/dvb-frontends/dib0090.c

@@ -1285,7 +1285,7 @@ int dib0090_gain_control(struct dvb_frontend *fe)
 #endif
 
 			if (*tune_state == CT_AGC_STEP_1) {	/* quickly go to the correct range of the ADC power */
-				if (ABS(adc_error) < 50 || state->agc_step++ > 5) {
+				if (abs(adc_error) < 50 || state->agc_step++ > 5) {
 
 #ifdef CONFIG_STANDARD_DAB
 					if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) {
@@ -1754,7 +1754,7 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
 			*tune_state = CT_TUNER_STEP_1;
 		} else {
 			/* the minimum was what we have seen in the step before */
-			if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) {
+			if (abs(state->adc_diff) > abs(state->min_adc_diff)) {
 				dprintk("Since adc_diff N = %d  > adc_diff step N-1 = %d, Come back one step\n", state->adc_diff, state->min_adc_diff);
 				state->step--;
 			}

+ 1 - 1
drivers/media/dvb-frontends/dib7000p.c

@@ -809,7 +809,7 @@ static int dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz)
 {
 	u32 internal = dib7000p_get_internal_freq(state);
 	s32 unit_khz_dds_val;
-	u32 abs_offset_khz = ABS(offset_khz);
+	u32 abs_offset_khz = abs(offset_khz);
 	u32 dds = state->cfg.bw->ifreq & 0x1ffffff;
 	u8 invert = !!(state->cfg.bw->ifreq & (1 << 25));
 	if (internal == 0) {

+ 1 - 1
drivers/media/dvb-frontends/dib8000.c

@@ -2677,7 +2677,7 @@ static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff)
 static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz)
 {
 	s16 unit_khz_dds_val;
-	u32 abs_offset_khz = ABS(offset_khz);
+	u32 abs_offset_khz = abs(offset_khz);
 	u32 dds = state->cfg.pll->ifreq & 0x1ffffff;
 	u8 invert = !!(state->cfg.pll->ifreq & (1 << 25));
 	u8 ratio;

+ 1 - 1
drivers/media/dvb-frontends/dibx000_common.c

@@ -424,7 +424,7 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap,
 				struct i2c_algorithm *algo, const char *name,
 				struct dibx000_i2c_master *mst)
 {
-	strncpy(i2c_adap->name, name, sizeof(i2c_adap->name));
+	strlcpy(i2c_adap->name, name, sizeof(i2c_adap->name));
 	i2c_adap->algo = algo;
 	i2c_adap->algo_data = NULL;
 	i2c_set_adapdata(i2c_adap, mst);

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.