linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
@ 2017-10-13  5:46 Yasunari.Takiguchi
  2017-10-13  5:58 ` [PATCH v4 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
                   ` (13 more replies)
  0 siblings, 14 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  5:46 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari.Takiguchi, Masayuki.Yamamoto,
	Hideki.Nozawa, Kota.Yonezawa, Toshihiko.Matsumoto,
	Satoshi.C.Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Hi,

This is the patch series (version 4) of Sony CXD2880 DVB-T2/T tuner + 
demodulator driver.The driver supports DVB-API and interfaces through 
SPI.

We have tested the driver on Raspberry Pi 3 and got picture and sound 
from a media player.

The change history of this patch series is as below.

[Change list]
Changes in V4
(1)Total patch number was changed from 14 to 12.
     We put [PATCH v3 12/14], [PATCH v3 13/14] and [PATCH v3 14/14]
   in [PATCH v4 12/12].

(2)Removed another file.
     These below files were removed because we changed it so that
   demodulator does not wait for locking the signal.

    [PATCH v4 09/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
          -removed
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
          -removed
    [PATCH v4 11/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
          -removed
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
          -removed

(3)The detail change items of each files are as below.
    [PATCH v4 02/12]
       drivers/media/spi/cxd2880-spi.c
          -removed Camel case
          -removed unnecessary initialization at variable declaration
          -removed unnecessary brace {}

    [PATCH v4 03/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
          -removed unnecessary initialization at variable declaration
          -modified how to write consecutive registers

    [PATCH v4 04/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
          -removed unnecessary initialization at variable declaration

    [PATCH v4 05/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
          -used over 80 columns limit, it makes fine to read codes
          -removed unnecessary initialization at variable declaration
          -modified how to write consecutive registers
          -removed unnecessary brace {}
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
          -adjusted of indent spaces of macro
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
          -updated version information
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
          -removed unnecessary brace {}

    [PATCH v4 06/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
          -removed unnecessary initialization at variable declaration

    [PATCH v4 07/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
          -modified typo "inavlid" to "invalid" at pr_err
          -removed unnecessary initialization at variable declaration
          -removed unnecessary brace {}
          -changed to use cxd2880_dvbt_tune and cxd2880_dvbt2_tune 
           instead of cxd2880_integ_dvbt_tune and cxd2880_integ_dvbt2_tune
            (because we changed it so that demodulator does not 
             wait for locking the signal.) 

    [PATCH v4 08/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
          -used over 80 columns limit, it makes fine to read codes
          -removed unnecessary initialization at variable declaration
          -removed unnecessary brace {}
          -modified how to write consecutive registers

    [PATCH v4 09/12]
       #drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
          -cxd2880_integ_dvbt.c file was removed from V4.
       #drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
          -cxd2880_integ_dvbt.h file was removed from V4.
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
          -removed unnecessary initialization at variable declaration
          -removed unnecessary brace {}
          -changed position of static const (to top part of the file)

    [PATCH v4 10/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
          -removed unnecessary initialization at variable declaration
          -removed unnecessary brace {}
          -modified how to write consecutive registers

    [PATCH v4 11/12]
       #drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
          -cxd2880_integ_dvbt2.c file was removed from V4.
       #drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
          -cxd2880_integ_dvbt2.h file was removed from V4.
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
          -removed unnecessary initialization at variable declaration
          -removed unnecessary brace {}
          -changed position of static const (to top part of the file)

    [PATCH v4 12/12]
       drivers/media/dvb-frontends/cxd2880/Makefile
          -removed cxd2880_integ_dvbt2.o and cxd2880_integ_dvbt.o 

Changes in V3
(1)Total patch number was changed from 15 to 14,
   due to the all files of [PATCH v2 04/15] were removed.
       drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
          -Removed
       drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
          -Removed

(2)Removed another file.
       drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
          -Removed 

(3)The detail change items of each files are as below.
    [PATCH v3 01/14]
       Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
          -no change
    [PATCH v3 02/14]
       drivers/media/spi/cxd2880-spi.c
          -adjusted of indent spaces
          -removed unnecessary cast
          -changed debugging code
          -changed timeout method
          -modified coding style of if()
          -changed hexadecimal code to lower case. 
    [PATCH v3 03/14]
       drivers/media/dvb-frontends/cxd2880/cxd2880.h
          -no change
       drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
          -changed MASKUPPER/MASKLOWER with GENMASK 
       drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
          -removed definition NULL and SONY_SLEEP
          -changed CXD2880_SLEEP to usleep_range
          -changed cxd2880_atomic_set to atomic_set
          -removed cxd2880_atomic struct and cxd2880_atomic_read
          -changed stop-watch function
          -modified return code
       drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
          -removed unnecessary cast
          -modified return code
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
          -modified return code 
       drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
          -changed CXD2880_SLEEP to usleep_range
          -changed stop-watch function
          -modified return code
       #drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
          -cxd2880_stdlib.h file was removed from V3.
    [PATCH v3 04/14]
       drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
          -removed unnecessary cast
          -changed cxd2880_memcpy to memcpy
          -modified return code
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
          -modified return code
       drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
          -modified return code
       drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
          -removed unnecessary cast
          -modified return code
       drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
          -modified return code
    [PATCH v3 05/14]
       drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
          -removed code relevant to ISDB-T
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
          -removed unnecessary cast
          -removed code relevant to ISDB-T
          -changed CXD2880_SLEEP to usleep_range
          -changed cxd2880_memset to memset 
          -changed cxd2880_atomic_set to atomic_set
          -modified return code
          -modified coding style of if()
          -changed to use const values at writing a lot of registers 
           with a command. 
          -changed hexadecimal code to lower case. 
          -adjusted of indent spaces
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
          -removed code relevant to ISDB-T
          -changed cxd2880_atomic struct to atomic_t
          -modified return code
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
          -updated version information
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
          -changed CXD2880_SLEEP to usleep_range
          -removed unnecessary cast
          -modified return code
          -modified coding style of if() 
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
          -modified return code
    [PATCH v3 06/14]
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
          -changed cxd2880_atomic_read to atomic_read
          -changed cxd2880_atomic_set to atomic_set
          -modified return code
          -modified coding style of if() 
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
          -modified return code
    [PATCH v3 07/14]
       drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
          -adjusted indent spaces
          -modified debugging code
          -removed unnecessary cast
          -modified return code
          -modified coding style of if() 
          -modified about measurement period of PER/BER.
          -changed hexadecimal code to lower case. 
    [PATCH v3 08/14]
       drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
          -no change
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
          -modified return code
          -modified coding style of if() 
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
          -modified return code
    [PATCH v3 09/14]
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
          -changed CXD2880_SLEEP to usleep_range
          -chnaged cxd2880_atomic_set to atomic_set
          -modified return code
          -modified coding style of if() 
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
          -modified return code
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
          -removed unnecessary cast
          -changed cxd2880_math_log to intlog10
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
          -modified return code
    [PATCH v3 10/14]
       drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
          -modified return code
          -modified coding style of if() 
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
          -modified return code
    [PATCH v3 11/14]
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
          -changed CXD2880_SLEEP to usleep_range
          -replaced cxd2880_atomic_set to atomic_set
          -modified return code
          -modified coding style of if()  
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
          -modified return code
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
          -removed unnecessary cast
          -changed cxd2880_math_log to intlog10
          -modified return code
          -modified coding style of if() 
          -changed hexadecimal code to lower case. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
          -modified return code
    [PATCH v3 12/14]
       drivers/media/dvb-frontends/Makefile
          -no change
       drivers/media/dvb-frontends/cxd2880/Makefile
          -removed cxd2880_math.o 
       drivers/media/spi/Makefile
          -no change
    [PATCH v3 13/14]
       drivers/media/dvb-frontends/Kconfig
          -no change
       drivers/media/dvb-frontends/cxd2880/Kconfig
          -no change
       drivers/media/spi/Kconfig
          -no change
    [PATCH v3 14/14]
       MAINTAINERS
          -no change

Changes in V2
(1)[PATCH 2/5], [PATCH 3/5] and [PATCH 4/5] of version 1 
   were divided to change order and be small size patch.
    Total patch number was changed from 5 to 15

   <Previous>
   The changed or created files of version 1 
   [PATCH 2/5], [PATCH 3/5] and [PATCH 4/5]:
      [PATCH 2/5]
      drivers/media/spi/Kconfig
      drivers/media/spi/Makefile
      drivers/media/spi/cxd2880-spi.c
      [PATCH 3/5]
      drivers/media/dvb-frontends/Kconfig
      drivers/media/dvb-frontends/Makefile
      drivers/media/dvb-frontends/cxd2880/Kconfig
      drivers/media/dvb-frontends/cxd2880/Makefile
      drivers/media/dvb-frontends/cxd2880/cxd2880.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
    [PATCH 4/5]
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h

   <New>
   The changed or created files of version 2 
   from [PATCH v2 02/15] to [PATCH v2 14/15]:
    [PATCH v2 02/15]
      drivers/media/spi/cxd2880-spi.c
    [PATCH v2 03/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
    [PATCH v2 04/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
    [PATCH v2 05/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
    [PATCH v2 06/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
    [PATCH v2 07/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
    [PATCH v2 08/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
    [PATCH v2 09/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
    [PATCH v2 10/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
    [PATCH v2 11/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
    [PATCH v2 12/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
    [PATCH v2 13/15]
      drivers/media/dvb-frontends/Makefile
      drivers/media/dvb-frontends/cxd2880/Makefile
      drivers/media/spi/Makefile
    [PATCH v2 14/15]
      drivers/media/dvb-frontends/Kconfig
      drivers/media/dvb-frontends/cxd2880/Kconfig
      drivers/media/spi/Kconfig

(2)Modified PID filter setting.
    drivers/media/spi/cxd2880-spi.c in [PATCH v2 02/15]

(3)Driver version up
    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
    in [PATCH v2 06/15]

Thanks,
Takiguchi
---
 Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt        | 14 ++++++++++++++
 drivers/media/spi/cxd2880-spi.c                                     | 695 ++++++++++++++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880.h                       | 46 +++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c                | 38 +++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h                | 50 ++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c                    | 89 ++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h                    | 71 +++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c        | 60 +++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c             | 146 +++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h             |  40 ++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h                   |  51 +++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c            | 130 ++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h            |  43 ++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h                   |   46 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c                | 3687 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h                |  391 +++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h |   29 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c            |  218 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h            |   52 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c                 | 98 ++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h                 | 44 ++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c                   | 2019 +++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h                  |  91 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c           | 954 +++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h           |  62 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c       | 1201 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h       |  106 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h                 |  402 +++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c          | 1260 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h          |   82 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c      | 2535 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h      |  170 ++
 MAINTAINERS                                                         |  9 +++++++++
 drivers/media/dvb-frontends/Kconfig                                 |  2 ++
 drivers/media/dvb-frontends/Makefile                                |  1 +
 drivers/media/dvb-frontends/cxd2880/Kconfig                         |  6 ++++++
 drivers/media/dvb-frontends/cxd2880/Makefile                        | 18 ++++++++++++++++++
 drivers/media/spi/Kconfig                                           | 14 ++++++++++++++
 drivers/media/spi/Makefile                                          |  5 +++++

 39 files changed, 14975 insertions(+)

 create mode 100644 Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
 create mode 100644 drivers/media/spi/cxd2880-spi.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/Kconfig
 create mode 100644 drivers/media/dvb-frontends/cxd2880/Makefile
2.11.0

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

* [PATCH v4 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
@ 2017-10-13  5:58 ` Yasunari.Takiguchi
  2017-10-13  5:59 ` [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Yasunari.Takiguchi
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  5:58 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This is the document file for Sony CXD2880 DVB-T2/T tuner + demodulator.
It contains the description of the SPI adapter binding.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
Acked-by: Rob Herring <robh@kernel.org>
---

No change since version 1.

 .../devicetree/bindings/media/spi/sony-cxd2880.txt         | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt

diff --git a/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt b/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
new file mode 100644
index 000000000000..fc5aa263abe5
--- /dev/null
+++ b/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 */
+};
-- 
2.13.0

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

* [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
  2017-10-13  5:58 ` [PATCH v4 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
@ 2017-10-13  5:59 ` Yasunari.Takiguchi
  2017-10-13  8:44   ` Honza Petrouš
  2017-12-13 17:54   ` Mauro Carvalho Chehab
  2017-10-13  6:02 ` [PATCH v4 03/12] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
                   ` (11 subsequent siblings)
  13 siblings, 2 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  5:59 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This is the SPI adapter part of the driver for the
Sony CXD2880 DVB-T2/T tuner + demodulator.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   drivers/media/spi/cxd2880-spi.c
      -removed Camel case
      -removed unnecessary initialization at variable declaration
      -removed unnecessary brace {}

Changes in V3
   drivers/media/spi/cxd2880-spi.c
      -adjusted of indent spaces
      -removed unnecessary cast
      -changed debugging code
      -changed timeout method
      -modified coding style of if()
      -changed hexadecimal code to lower case. 

Changes in V2
   drivers/media/spi/cxd2880-spi.c
      -Modified PID filter setting.

 drivers/media/spi/cxd2880-spi.c | 695 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 695 insertions(+)
 create mode 100644 drivers/media/spi/cxd2880-spi.c

diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
new file mode 100644
index 000000000000..387cb32f90b8
--- /dev/null
+++ b/drivers/media/spi/cxd2880-spi.c
@@ -0,0 +1,695 @@
+/*
+ * cxd2880-spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI adapter
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+#include <linux/spi/spi.h>
+#include <linux/jiffies.h>
+
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_frontend.h"
+#include "cxd2880.h"
+
+#define CXD2880_MAX_FILTER_SIZE 32
+#define BURST_WRITE_MAX 128
+#define MAX_TRANS_PACKET 300
+
+struct cxd2880_ts_buf_info {
+	u8 read_ready;
+	u8 almost_full;
+	u8 almost_empty;
+	u8 overflow;
+	u8 underflow;
+	u16 packet_num;
+};
+
+struct cxd2880_pid_config {
+	u8 is_enable;
+	u16 pid;
+};
+
+struct cxd2880_pid_filter_config {
+	u8 is_negative;
+	struct cxd2880_pid_config pid_config[CXD2880_MAX_FILTER_SIZE];
+};
+
+struct cxd2880_dvb_spi {
+	struct dvb_frontend dvb_fe;
+	struct dvb_adapter adapter;
+	struct dvb_demux demux;
+	struct dmxdev dmxdev;
+	struct dmx_frontend dmx_fe;
+	struct task_struct *cxd2880_ts_read_thread;
+	struct spi_device *spi;
+	struct mutex spi_mutex; /* For SPI access exclusive control */
+	int feed_count;
+	int all_pid_feed_count;
+	u8 *ts_buf;
+	struct cxd2880_pid_filter_config filter_config;
+};
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size)
+{
+	struct spi_message msg;
+	struct spi_transfer tx;
+	int ret;
+
+	if ((!spi) || (!data)) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	memset(&tx, 0, sizeof(tx));
+	tx.tx_buf = data;
+	tx.len = size;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&tx, &msg);
+	ret = spi_sync(spi, &msg);
+
+	return ret;
+}
+
+static int cxd2880_write_reg(struct spi_device *spi,
+			     u8 sub_address, const u8 *data, u32 size)
+{
+	u8 send_data[BURST_WRITE_MAX + 4];
+	const u8 *write_data_top = NULL;
+	int ret = 0;
+
+	if ((!spi) || (!data)) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+	if (size > BURST_WRITE_MAX) {
+		pr_err("data size > WRITE_MAX\n");
+		return -EINVAL;
+	}
+
+	if (sub_address + size > 0x100) {
+		pr_err("out of range\n");
+		return -EINVAL;
+	}
+
+	send_data[0] = 0x0e;
+	write_data_top = data;
+
+	while (size > 0) {
+		send_data[1] = sub_address;
+		if (size > 255)
+			send_data[2] = 255;
+		else
+			send_data[2] = (u8)size;
+
+		memcpy(&send_data[3], write_data_top, send_data[2]);
+
+		ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3);
+		if (ret) {
+			pr_err("write spi failed %d\n", ret);
+			break;
+		}
+		sub_address += send_data[2];
+		write_data_top += send_data[2];
+		size -= send_data[2];
+	}
+
+	return ret;
+}
+
+static int cxd2880_spi_read_ts(struct spi_device *spi,
+			       u8 *read_data,
+			       u32 packet_num)
+{
+	int ret;
+	u8 data[3];
+	struct spi_message message;
+	struct spi_transfer transfer[2];
+
+	if ((!spi) || (!read_data) || (!packet_num)) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+	if (packet_num > 0xffff) {
+		pr_err("packet num > 0xffff\n");
+		return -EINVAL;
+	}
+
+	data[0] = 0x10;
+	data[1] = (packet_num >> 8) & 0xff;
+	data[2] = packet_num & 0xff;
+
+	spi_message_init(&message);
+	memset(transfer, 0, sizeof(transfer));
+
+	transfer[0].len = 3;
+	transfer[0].tx_buf = data;
+	spi_message_add_tail(&transfer[0], &message);
+	transfer[1].len = packet_num * 188;
+	transfer[1].rx_buf = read_data;
+	spi_message_add_tail(&transfer[1], &message);
+
+	ret = spi_sync(spi, &message);
+	if (ret)
+		pr_err("spi_write_then_read failed\n");
+
+	return ret;
+}
+
+static int cxd2880_spi_read_ts_buffer_info(struct spi_device *spi,
+					   struct cxd2880_ts_buf_info *info)
+{
+	u8 send_data = 0x20;
+	u8 recv_data[2];
+	int ret;
+
+	if ((!spi) || (!info)) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	ret = spi_write_then_read(spi, &send_data, 1,
+				  recv_data, sizeof(recv_data));
+	if (ret)
+		pr_err("spi_write_then_read failed\n");
+
+	info->read_ready = (recv_data[0] & 0x80) ? 1 : 0;
+	info->almost_full = (recv_data[0] & 0x40) ? 1 : 0;
+	info->almost_empty = (recv_data[0] & 0x20) ? 1 : 0;
+	info->overflow = (recv_data[0] & 0x10) ? 1 : 0;
+	info->underflow = (recv_data[0] & 0x08) ? 1 : 0;
+	info->packet_num = ((recv_data[0] & 0x07) << 8) | recv_data[1];
+
+	return ret;
+}
+
+static int cxd2880_spi_clear_ts_buffer(struct spi_device *spi)
+{
+	u8 data = 0x03;
+	int ret;
+
+	ret = cxd2880_write_spi(spi, &data, 1);
+
+	if (ret)
+		pr_err("write spi failed\n");
+
+	return ret;
+}
+
+static int cxd2880_set_pid_filter(struct spi_device *spi,
+				  struct cxd2880_pid_filter_config *cfg)
+{
+	u8 data[65];
+	int i;
+	u16 pid = 0;
+	int ret;
+
+	if (!spi) {
+		pr_err("ivnalid arg\n");
+		return -EINVAL;
+	}
+
+	data[0] = 0x00;
+	ret = cxd2880_write_reg(spi, 0x00, &data[0], 1);
+	if (ret)
+		return ret;
+	if (!cfg) {
+		data[0] = 0x02;
+		ret = cxd2880_write_reg(spi, 0x50, &data[0], 1);
+		if (ret)
+			return ret;
+	} else {
+		data[0] = cfg->is_negative ? 0x01 : 0x00;
+
+		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
+			pid = cfg->pid_config[i].pid;
+			if (cfg->pid_config[i].is_enable) {
+				data[1 + (i * 2)] = (pid >> 8) | 0x20;
+				data[2 + (i * 2)] = pid & 0xff;
+			} else {
+				data[1 + (i * 2)] = 0x00;
+				data[2 + (i * 2)] = 0x00;
+			}
+		}
+		ret = cxd2880_write_reg(spi, 0x50, data, 65);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int cxd2880_update_pid_filter(struct cxd2880_dvb_spi *dvb_spi,
+				     struct cxd2880_pid_filter_config *cfg,
+				     bool is_all_pid_filter)
+{
+	int ret;
+
+	if ((!dvb_spi) || (!cfg)) {
+		pr_err("invalid arg.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dvb_spi->spi_mutex);
+	if (is_all_pid_filter) {
+		struct cxd2880_pid_filter_config tmpcfg;
+
+		memset(&tmpcfg, 0, sizeof(tmpcfg));
+		tmpcfg.is_negative = 1;
+		tmpcfg.pid_config[0].is_enable = 1;
+		tmpcfg.pid_config[0].pid = 0x1fff;
+
+		ret = cxd2880_set_pid_filter(dvb_spi->spi, &tmpcfg);
+	} else {
+		ret = cxd2880_set_pid_filter(dvb_spi->spi, cfg);
+	}
+	mutex_unlock(&dvb_spi->spi_mutex);
+
+	if (ret)
+		pr_err("set_pid_filter failed\n");
+
+	return ret;
+}
+
+static int cxd2880_ts_read(void *arg)
+{
+	struct cxd2880_dvb_spi *dvb_spi = NULL;
+	struct cxd2880_ts_buf_info info;
+	unsigned int elapsed = 0;
+	unsigned long start_time = 0;
+	u32 i;
+	int ret;
+
+	dvb_spi = arg;
+	if (!dvb_spi) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi);
+	if (ret) {
+		pr_err("set_clear_ts_buffer failed\n");
+		return ret;
+	}
+	start_time = jiffies;
+	while (!kthread_should_stop()) {
+		elapsed = jiffies_to_msecs(jiffies - start_time);
+		ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi,
+						      &info);
+		if (ret) {
+			pr_err("spi_read_ts_buffer_info error\n");
+			return ret;
+		}
+
+		if (info.packet_num > MAX_TRANS_PACKET) {
+			for (i = 0; i < info.packet_num / MAX_TRANS_PACKET;
+				i++) {
+				cxd2880_spi_read_ts(dvb_spi->spi,
+						    dvb_spi->ts_buf,
+						    MAX_TRANS_PACKET);
+				dvb_dmx_swfilter(&dvb_spi->demux,
+						 dvb_spi->ts_buf,
+						 MAX_TRANS_PACKET * 188);
+			}
+			start_time = jiffies;
+		} else if ((info.packet_num > 0) && (elapsed >= 500)) {
+			cxd2880_spi_read_ts(dvb_spi->spi,
+					    dvb_spi->ts_buf,
+					    info.packet_num);
+			dvb_dmx_swfilter(&dvb_spi->demux,
+					 dvb_spi->ts_buf,
+					 info.packet_num * 188);
+			start_time = jiffies;
+		} else {
+			usleep_range(10000, 11000);
+		}
+	}
+
+	return 0;
+}
+
+static int cxd2880_start_feed(struct dvb_demux_feed *feed)
+{
+	int ret = 0;
+	int i = 0;
+	struct dvb_demux *demux = NULL;
+	struct cxd2880_dvb_spi *dvb_spi = NULL;
+
+	if (!feed) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	demux = feed->demux;
+	if (!demux) {
+		pr_err("feed->demux is NULL\n");
+		return -EINVAL;
+	}
+	dvb_spi = demux->priv;
+
+	if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) {
+		pr_err("Exceeded maximum PID count (32).");
+		pr_err("Selected PID cannot be enabled.\n");
+		return -EBUSY;
+	}
+
+	if (feed->pid == 0x2000) {
+		if (dvb_spi->all_pid_feed_count == 0) {
+			ret = cxd2880_update_pid_filter(dvb_spi,
+							&dvb_spi->filter_config,
+							true);
+			if (ret) {
+				pr_err("update pid filter failed\n");
+				return ret;
+			}
+		}
+		dvb_spi->all_pid_feed_count++;
+
+		pr_debug("all PID feed (count = %d)\n",
+			 dvb_spi->all_pid_feed_count);
+	} else {
+		struct cxd2880_pid_filter_config cfgtmp;
+
+		cfgtmp = dvb_spi->filter_config;
+
+		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
+			if (cfgtmp.pid_config[i].is_enable == 0) {
+				cfgtmp.pid_config[i].is_enable = 1;
+				cfgtmp.pid_config[i].pid = feed->pid;
+				pr_debug("store PID %d to #%d\n",
+					 feed->pid, i);
+				break;
+			}
+		}
+		if (i == CXD2880_MAX_FILTER_SIZE) {
+			pr_err("PID filter is full. Assumed bug.\n");
+			return -EBUSY;
+		}
+		if (!dvb_spi->all_pid_feed_count)
+			ret = cxd2880_update_pid_filter(dvb_spi,
+							&cfgtmp,
+							false);
+		if (ret)
+			return ret;
+
+		dvb_spi->filter_config = cfgtmp;
+	}
+
+	if (dvb_spi->feed_count == 0) {
+		dvb_spi->ts_buf =
+			kmalloc(MAX_TRANS_PACKET * 188,
+				GFP_KERNEL | GFP_DMA);
+		if (!dvb_spi->ts_buf) {
+			pr_err("ts buffer allocate failed\n");
+			memset(&dvb_spi->filter_config, 0,
+			       sizeof(dvb_spi->filter_config));
+			dvb_spi->all_pid_feed_count = 0;
+			return -ENOMEM;
+		}
+		dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read,
+							      dvb_spi,
+							      "cxd2880_ts_read");
+		if (IS_ERR(dvb_spi->cxd2880_ts_read_thread)) {
+			pr_err("kthread_run failed/\n");
+			kfree(dvb_spi->ts_buf);
+			dvb_spi->ts_buf = NULL;
+			memset(&dvb_spi->filter_config, 0,
+			       sizeof(dvb_spi->filter_config));
+			dvb_spi->all_pid_feed_count = 0;
+			return PTR_ERR(dvb_spi->cxd2880_ts_read_thread);
+		}
+	}
+
+	dvb_spi->feed_count++;
+
+	pr_debug("start feed (count %d)\n", dvb_spi->feed_count);
+	return 0;
+}
+
+static int cxd2880_stop_feed(struct dvb_demux_feed *feed)
+{
+	int i = 0;
+	int ret;
+	struct dvb_demux *demux = NULL;
+	struct cxd2880_dvb_spi *dvb_spi = NULL;
+
+	if (!feed) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	demux = feed->demux;
+	if (!demux) {
+		pr_err("feed->demux is NULL\n");
+		return -EINVAL;
+	}
+	dvb_spi = demux->priv;
+
+	if (!dvb_spi->feed_count) {
+		pr_err("no feed is started\n");
+		return -EINVAL;
+	}
+
+	if (feed->pid == 0x2000) {
+		/*
+		 * Special PID case.
+		 * Number of 0x2000 feed request was stored
+		 * in dvb_spi->all_pid_feed_count.
+		 */
+		if (dvb_spi->all_pid_feed_count <= 0) {
+			pr_err("PID %d not found.\n", feed->pid);
+			return -EINVAL;
+		}
+		dvb_spi->all_pid_feed_count--;
+	} else {
+		struct cxd2880_pid_filter_config cfgtmp;
+
+		cfgtmp = dvb_spi->filter_config;
+
+		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
+			if (feed->pid == cfgtmp.pid_config[i].pid &&
+			    cfgtmp.pid_config[i].is_enable != 0) {
+				cfgtmp.pid_config[i].is_enable = 0;
+				cfgtmp.pid_config[i].pid = 0;
+				pr_debug("removed PID %d from #%d\n",
+					 feed->pid, i);
+				break;
+			}
+		}
+		dvb_spi->filter_config = cfgtmp;
+
+		if (i == CXD2880_MAX_FILTER_SIZE) {
+			pr_err("PID %d not found\n", feed->pid);
+			return -EINVAL;
+		}
+	}
+
+	ret = cxd2880_update_pid_filter(dvb_spi,
+					&dvb_spi->filter_config,
+					dvb_spi->all_pid_feed_count > 0);
+	dvb_spi->feed_count--;
+
+	if (dvb_spi->feed_count == 0) {
+		int ret_stop = 0;
+
+		ret_stop = kthread_stop(dvb_spi->cxd2880_ts_read_thread);
+		if (ret_stop) {
+			pr_err("'kthread_stop failed. (%d)\n", ret_stop);
+			ret = ret_stop;
+		}
+		kfree(dvb_spi->ts_buf);
+		dvb_spi->ts_buf = NULL;
+	}
+
+	pr_debug("stop feed ok.(count %d)\n", dvb_spi->feed_count);
+
+	return ret;
+}
+
+static const struct of_device_id cxd2880_spi_of_match[] = {
+	{ .compatible = "sony,cxd2880" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match);
+
+static int
+cxd2880_spi_probe(struct spi_device *spi)
+{
+	int ret;
+	struct cxd2880_dvb_spi *dvb_spi = NULL;
+	struct cxd2880_config config;
+
+	if (!spi) {
+		pr_err("invalid arg.\n");
+		return -EINVAL;
+	}
+
+	dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL);
+	if (!dvb_spi)
+		return -ENOMEM;
+
+	dvb_spi->spi = spi;
+	mutex_init(&dvb_spi->spi_mutex);
+	dev_set_drvdata(&spi->dev, dvb_spi);
+	config.spi = spi;
+	config.spi_mutex = &dvb_spi->spi_mutex;
+
+	ret = dvb_register_adapter(&dvb_spi->adapter,
+				   "CXD2880",
+				   THIS_MODULE,
+				   &spi->dev,
+				   adapter_nr);
+	if (ret < 0) {
+		pr_err("dvb_register_adapter() failed\n");
+		goto fail_adapter;
+	}
+
+	if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
+		pr_err("cxd2880_attach failed\n");
+		goto fail_attach;
+	}
+
+	ret = dvb_register_frontend(&dvb_spi->adapter,
+				    &dvb_spi->dvb_fe);
+	if (ret < 0) {
+		pr_err("dvb_register_frontend() failed\n");
+		goto fail_frontend;
+	}
+
+	dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING;
+	dvb_spi->demux.priv = dvb_spi;
+	dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE;
+	dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE;
+	dvb_spi->demux.start_feed = cxd2880_start_feed;
+	dvb_spi->demux.stop_feed = cxd2880_stop_feed;
+
+	ret = dvb_dmx_init(&dvb_spi->demux);
+	if (ret < 0) {
+		pr_err("dvb_dmx_init() failed\n");
+		goto fail_dmx;
+	}
+
+	dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE;
+	dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx;
+	dvb_spi->dmxdev.capabilities = 0;
+	ret = dvb_dmxdev_init(&dvb_spi->dmxdev,
+			      &dvb_spi->adapter);
+	if (ret < 0) {
+		pr_err("dvb_dmxdev_init() failed\n");
+		goto fail_dmxdev;
+	}
+
+	dvb_spi->dmx_fe.source = DMX_FRONTEND_0;
+	ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx,
+					      &dvb_spi->dmx_fe);
+	if (ret < 0) {
+		pr_err("add_frontend() failed\n");
+		goto fail_dmx_fe;
+	}
+
+	ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx,
+						  &dvb_spi->dmx_fe);
+	if (ret < 0) {
+		pr_err("dvb_register_frontend() failed\n");
+		goto fail_fe_conn;
+	}
+
+	pr_info("Sony CXD2880 has successfully attached.\n");
+
+	return 0;
+
+fail_fe_conn:
+	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
+					   &dvb_spi->dmx_fe);
+fail_dmx_fe:
+	dvb_dmxdev_release(&dvb_spi->dmxdev);
+fail_dmxdev:
+	dvb_dmx_release(&dvb_spi->demux);
+fail_dmx:
+	dvb_unregister_frontend(&dvb_spi->dvb_fe);
+fail_frontend:
+	dvb_frontend_detach(&dvb_spi->dvb_fe);
+fail_attach:
+	dvb_unregister_adapter(&dvb_spi->adapter);
+fail_adapter:
+	kfree(dvb_spi);
+	return ret;
+}
+
+static int
+cxd2880_spi_remove(struct spi_device *spi)
+{
+	struct cxd2880_dvb_spi *dvb_spi;
+
+	if (!spi) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	dvb_spi = dev_get_drvdata(&spi->dev);
+
+	if (!dvb_spi) {
+		pr_err("failed\n");
+		return -EINVAL;
+	}
+	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
+					   &dvb_spi->dmx_fe);
+	dvb_dmxdev_release(&dvb_spi->dmxdev);
+	dvb_dmx_release(&dvb_spi->demux);
+	dvb_unregister_frontend(&dvb_spi->dvb_fe);
+	dvb_frontend_detach(&dvb_spi->dvb_fe);
+	dvb_unregister_adapter(&dvb_spi->adapter);
+
+	kfree(dvb_spi);
+	pr_info("cxd2880_spi remove ok.\n");
+
+	return 0;
+}
+
+static const struct spi_device_id cxd2880_spi_id[] = {
+	{ "cxd2880", 0 },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, cxd2880_spi_id);
+
+static struct spi_driver cxd2880_spi_driver = {
+	.driver	= {
+		.name	= "cxd2880",
+		.of_match_table = cxd2880_spi_of_match,
+	},
+	.id_table = cxd2880_spi_id,
+	.probe    = cxd2880_spi_probe,
+	.remove   = cxd2880_spi_remove,
+};
+module_spi_driver(cxd2880_spi_driver);
+
+MODULE_DESCRIPTION(
+"Sony CXD2880 DVB-T2/T tuner + demodulator drvier SPI adapter");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
-- 
2.13.0

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

* [PATCH v4 03/12] [media] cxd2880: Add common files for the driver
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
  2017-10-13  5:58 ` [PATCH v4 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
  2017-10-13  5:59 ` [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Yasunari.Takiguchi
@ 2017-10-13  6:02 ` Yasunari.Takiguchi
  2017-12-13 18:02   ` Mauro Carvalho Chehab
  2017-10-13  6:05 ` [PATCH v4 04/12] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:02 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

These are common files for the driver for the
Sony CXD2880 DVB-T2/T tuner + demodulator.
These contains helper functions for the driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
      -removed unnecessary initialization at variable declaration
      -modified how to write consecutive registers

Changes in V3
   drivers/media/dvb-frontends/cxd2880/cxd2880.h
      -no change
   drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
      -changed MASKUPPER/MASKLOWER with GENMASK 
   drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
      -removed definition NULL and SONY_SLEEP
      -changed CXD2880_SLEEP to usleep_range
      -changed cxd2880_atomic_set to atomic_set
      -removed cxd2880_atomic struct and cxd2880_atomic_read
      -changed stop-watch function
      -modified return code
   drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
      -removed unnecessary cast
      -modified return code
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
      -modified return code 
   drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
      -changed CXD2880_SLEEP to usleep_range
      -changed stop-watch function
      -modified return code
   #drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
      -cxd2880_stdlib.h file was removed from V3.

 drivers/media/dvb-frontends/cxd2880/cxd2880.h      | 46 +++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_common.c   | 38 +++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_common.h   | 50 ++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c   | 89 ++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h   | 71 +++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_stopwatch_port.c | 60 +++++++++++++++
 6 files changed, 354 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
new file mode 100644
index 000000000000..281f9a784eb5
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
@@ -0,0 +1,46 @@
+/*
+ * cxd2880.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 */
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
new file mode 100644
index 000000000000..ffaa140bb8cb
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
@@ -0,0 +1,38 @@
+/*
+ * cxd2880_common.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_common.h"
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen)
+{
+	if ((bitlen == 0) || (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);
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
new file mode 100644
index 000000000000..cf6b800809ee
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
@@ -0,0 +1,50 @@
+/*
+ * cxd2880_common.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_COMMON_H
+#define CXD2880_COMMON_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+#define CXD2880_ARG_UNUSED(arg) ((void)(arg))
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen);
+
+struct cxd2880_stopwatch {
+	unsigned long start_time;
+};
+
+int cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch);
+
+int cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
+			    u32 ms);
+
+int cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
+			      unsigned int *elapsed);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
new file mode 100644
index 000000000000..bdb0b7982401
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
@@ -0,0 +1,89 @@
+/*
+ * cxd2880_io.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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)
+{
+	int ret;
+
+	if (!io)
+		return -EINVAL;
+
+	ret = io->write_regs(io, tgt, sub_address, &data, 1);
+
+	return ret;
+}
+
+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));
+	}
+
+	ret = io->write_reg(io, tgt, sub_address, data);
+
+	return ret;
+}
+
+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;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
new file mode 100644
index 000000000000..f7aee6c6480e
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
@@ -0,0 +1,71 @@
+/*
+ * cxd2880_io.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
new file mode 100644
index 000000000000..a4a1e29de653
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
@@ -0,0 +1,60 @@
+/*
+ * cxd2880_stopwatch_port.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * time measurement functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/jiffies.h>
+
+#include "cxd2880_common.h"
+
+int cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch)
+{
+	if (!stopwatch)
+		return -EINVAL;
+
+	stopwatch->start_time = jiffies;
+
+	return 0;
+}
+
+int cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
+			    u32 ms)
+{
+	if (!stopwatch)
+		return -EINVAL;
+	CXD2880_ARG_UNUSED(*stopwatch);
+	usleep_range(ms * 10000, ms * 10000 + 1000);
+
+	return 0;
+}
+
+int cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
+			      unsigned int *elapsed)
+{
+	if (!stopwatch || !elapsed)
+		return -EINVAL;
+	*elapsed = jiffies_to_msecs(jiffies - stopwatch->start_time);
+
+	return 0;
+}
-- 
2.13.0

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

* [PATCH v4 04/12] [media] cxd2880: Add spi device IO routines
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (2 preceding siblings ...)
  2017-10-13  6:02 ` [PATCH v4 03/12] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
@ 2017-10-13  6:05 ` Yasunari.Takiguchi
  2017-10-13  6:07 ` [PATCH v4 05/12] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:05 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Add functions for initializing, reading and writing to the SPI
device for the Sony CXD2880 DVB-T2/T tuner + demodulator.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
      -removed unnecessary initialization at variable declaration

Changes in V3
   drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
      -removed unnecessary cast
      -changed cxd2880_memcpy to memcpy
      -modified return code
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
      -modified return code
   drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
      -modified return code
   drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
      -removed unnecessary cast
      -modified return code
   drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
      -modified return code

 .../dvb-frontends/cxd2880/cxd2880_devio_spi.c      | 146 +++++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_devio_spi.h      |  40 ++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h  |  51 +++++++
 .../dvb-frontends/cxd2880/cxd2880_spi_device.c     | 130 ++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_spi_device.h     |  43 ++++++
 5 files changed, 410 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
new file mode 100644
index 000000000000..e6226a00fc8c
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
@@ -0,0 +1,146 @@
+/*
+ * cxd2880_devio_spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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;
+	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 -ERANGE;
+
+	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;
+	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 -EOVERFLOW;
+
+	if (sub_address + size > 0x100)
+		return -ERANGE;
+
+	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;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
new file mode 100644
index 000000000000..41a86f0f558e
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
@@ -0,0 +1,40 @@
+/*
+ * cxd2880_devio_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
new file mode 100644
index 000000000000..f9e28daa6a2c
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
@@ -0,0 +1,51 @@
+/*
+ * cxd2880_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
new file mode 100644
index 000000000000..60b14e4a4098
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
@@ -0,0 +1,130 @@
+/*
+ * cxd2880_spi_device.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 == 0) || (!rx_data) || (rx_size == 0))
+		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;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
new file mode 100644
index 000000000000..63f658e29343
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
@@ -0,0 +1,43 @@
+/*
+ * cxd2880_spi_device.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 */
-- 
2.13.0

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

* [PATCH v4 05/12] [media] cxd2880: Add tuner part of the driver
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (3 preceding siblings ...)
  2017-10-13  6:05 ` [PATCH v4 04/12] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
@ 2017-10-13  6:07 ` Yasunari.Takiguchi
  2017-12-13 18:40   ` Mauro Carvalho Chehab
  2017-10-13  6:08 ` [PATCH v4 06/12] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:07 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This part of the driver has the main routines to handle
the tuner and demodulator functionality.  The tnrdmd_mon.* files
have monitor functions for the driver.
This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
      -used over 80 columns limit, it makes fine to read codes
      -removed unnecessary initialization at variable declaration
      -modified how to write consecutive registers
      -removed unnecessary brace {}
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
      -adjusted of indent spaces of macro
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
      -updated version information
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
      -removed unnecessary brace {}

Changes in V3
   drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
      -removed code relevant to ISDB-T
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
      -removed unnecessary cast
      -removed code relevant to ISDB-T
      -changed CXD2880_SLEEP to usleep_range
      -changed cxd2880_memset to memset 
      -changed cxd2880_atomic_set to atomic_set
      -modified return code
      -modified coding style of if()
      -changed to use const values at writing a lot of registers 
       with a command. 
      -changed hexadecimal code to lower case. 
      -adjusted of indent spaces
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
      -removed code relevant to ISDB-T
      -changed cxd2880_atomic struct to atomic_t
      -modified return code
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
      -updated version information
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
      -changed CXD2880_SLEEP to usleep_range
      -removed unnecessary cast
      -modified return code
      -modified coding style of if() 
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
      -modified return code

Changes in V2
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
      -updated version information

 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h  |   46 +
 .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c   | 3687 ++++++++++++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h   |  391 +++
 .../cxd2880/cxd2880_tnrdmd_driver_version.h        |   29 +
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c     |  218 ++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h     |   52 +
 6 files changed, 4423 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
new file mode 100644
index 000000000000..2d35d3990060
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
@@ -0,0 +1,46 @@
+/*
+ * cxd2880_dtv.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DTV related definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
new file mode 100644
index 000000000000..17fd0bc6249c
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
@@ -0,0 +1,3687 @@
+/*
+ * cxd2880_tnrdmd.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "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 -EOPNOTSUPP;
+	}
+
+	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 -EOPNOTSUPP;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_SYS,
+				     0x69, data);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x13,
+				      data,
+				      6);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x1f, data, 2);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 -EBUSY;
+
+	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 -EBUSY;
+
+	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 -EBUSY;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  rf_init1_seq9,
+					  ARRAY_SIZE(rf_init1_seq9));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  rf_init2_seq2,
+					  ARRAY_SIZE(rf_init2_seq2));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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[0] = 0x00;
+	data[1] = 0x00;
+	data[2] = 0x0e;
+	data[3] = 0x00;
+	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;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x52, data, 11);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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;
+	}
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  x_tune2_seq5,
+					  ARRAY_SIZE(x_tune2_seq5));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xb1, data[0]);
+	if (ret)
+		return ret;
+
+	return 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;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->diver_sub->io,
+					  CXD2880_IO_TGT_DMD,
+					  x_tune4_seq,
+					  ARRAY_SIZE(x_tune4_seq));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x55, data, 2);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 -EBUSY;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  x_sleep2_seq2,
+					  ARRAY_SIZE(x_sleep2_seq2));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int x_sleep3(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,
+					  x_sleep3_seq,
+					  ARRAY_SIZE(x_sleep3_seq));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int x_sleep4(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,
+					  x_sleep4_seq,
+					  ARRAY_SIZE(x_sleep4_seq));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 -EBUSY;
+
+	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));
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x26, data, 4);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 -EBUSY;
+
+		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 -EBUSY;
+	}
+
+	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 -EBUSY;
+
+	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));
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x27, data, 3);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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);
+		if (ret)
+			return ret;
+	}
+
+	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;
+
+	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) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x50, 0x02);
+		if (ret)
+			return ret;
+	} else {
+		u8 data[65];
+
+		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;
+			}
+		}
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x50, data, 65);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+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 ret;
+}
+
+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 == 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 -EOVERFLOW;
+		}
+	}
+
+	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 -EOPNOTSUPP;
+
+	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 -EOPNOTSUPP;
+	}
+
+	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);
+		if (ret)
+			return ret;
+	}
+
+	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 -EBUSY;
+
+	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 -EPERM;
+
+	if (frequency_khz < 4000)
+		return -ERANGE;
+
+	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 -EBUSY;
+
+	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);
+		if (ret)
+			return ret;
+	} 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);
+		if (ret)
+			return ret;
+	}
+
+	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 -EPERM;
+
+	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;
+	}
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_tune4(tnr_dmd);
+		if (ret)
+			return ret;
+	}
+
+	ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	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 -EPERM;
+	}
+
+	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;
+	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 -EPERM;
+
+	switch (id) {
+	case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return -EPERM;
+
+		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 -EPERM;
+
+		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 -EPERM;
+
+		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 -EPERM;
+
+		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 -EPERM;
+
+		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 -EPERM;
+
+		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 -EPERM;
+
+		if ((value < 0) || (value > 0x1f))
+			return -ERANGE;
+
+		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 -EPERM;
+
+		if ((value < 0) || (value > 0x1f))
+			return -ERANGE;
+
+		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 -EPERM;
+
+		if ((value < 0) || (value > 0x1f))
+			return -ERANGE;
+
+		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 -EPERM;
+
+		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 -EPERM;
+
+		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 -EPERM;
+
+		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 -EPERM;
+
+		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 -EPERM;
+
+		if ((value < 0) || (value > 1))
+			return -ERANGE;
+
+		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 -EPERM;
+
+		if ((value < 0) || (value > 0xff))
+			return -ERANGE;
+
+		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 -EPERM;
+
+		if ((value < 0) || (value > 7))
+			return -ERANGE;
+
+		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 -EPERM;
+
+		tnr_dmd->is_ts_backwards_compatible_mode = value ? 1 : 0;
+
+		break;
+
+	case CXD2880_TNRDMD_CFG_PWM_VALUE:
+		if ((value < 0) || (value > 0x1000))
+			return -ERANGE;
+
+		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 -ERANGE;
+		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 -ERANGE;
+
+		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 -ERANGE;
+
+		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 -ERANGE;
+
+		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 -ERANGE;
+
+		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 -ERANGE;
+
+		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 -ERANGE;
+
+		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);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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;
+
+	ret =
+	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+						 0x00, 0x45,
+						 en ? 0 : (1 << id),
+						 1 << id);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode,
+					open_drain, invert);
+
+	return ret;
+}
+
+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 -EPERM;
+
+	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)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+			      u8 id, u8 value)
+{
+	int ret;
+
+	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 -EPERM;
+
+	ret = cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+						   CXD2880_IO_TGT_SYS,
+						   0x00, 0x46,
+						   value ? (1 << id) : 0,
+						   1 << id);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				  u8 id, u8 value)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value);
+
+	return ret;
+}
+
+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 -EPERM;
+
+	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 -EPERM;
+
+	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;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_SYS,
+				      0x3c, data, 2);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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;
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x9f, data, 2);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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;
+
+	ret = set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	CXD2880_ARG_UNUSED(sys);
+
+	tnr_dmd->scan_mode = scan_mode_end;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		int ret;
+
+		ret =
+		    cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys,
+						 scan_mode_end);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_tnrdmd_pid_ftr_cfg
+			       *pid_ftr_cfg)
+{
+	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 -EPERM;
+
+	if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS)
+		return -EOPNOTSUPP;
+
+	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) {
+		ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+		if (ret)
+			return ret;
+	}
+
+	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 *))
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub,
+					       rf_lvl_cmpstn);
+
+	return ret;
+}
+
+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)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub,
+					  tbl_air, tbl_cable);
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS)
+		return -EOPNOTSUPP;
+
+	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));
+		if (ret)
+			return ret;
+	} 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);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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 -EPERM;
+	}
+
+	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;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x01, 0x01);
+	if (ret)
+		return ret;
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
new file mode 100644
index 000000000000..d740da0ff868
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
@@ -0,0 +1,391 @@
+/*
+ * cxd2880_tnrdmd.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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_ts_buf_info {
+	u8 read_ready;
+	u8 almost_full;
+	u8 almost_empty;
+	u8 overflow;
+	u8 underflow;
+	u16 packet_num;
+};
+
+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
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
new file mode 100644
index 000000000000..ff29e747d5b7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
@@ -0,0 +1,29 @@
+/*
+ * cxd2880_tnrdmd_driver_version.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * version information
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.3"
+
+#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2017-10-03"
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
new file mode 100644
index 000000000000..2b40b25ee2b8
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
@@ -0,0 +1,218 @@
+/*
+ * cxd2880_tnrdmd_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 -EPERM;
+
+	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] != 0) || (rdata[1] != 0))
+		return -EPERM;
+
+	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);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
+				  int *rf_lvl_db)
+{
+	int ret;
+
+	if ((!tnr_dmd) || (!rf_lvl_db))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db);
+
+	return ret;
+}
+
+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)
+{
+	int ret;
+
+	if ((!tnr_dmd) || (!status))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub, status);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
+				   struct
+				   cxd2880_tnrdmd_ts_buf_info
+				   *info)
+{
+	u8 data[3] = { 0 };
+	int ret;
+
+	if ((!tnr_dmd) || (!info))
+		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 -EPERM;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0a);
+	if (ret)
+		return ret;
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x50, data, 3);
+	if (ret)
+		return ret;
+
+	info->read_ready = (data[0] & 0x10) ? 0x01 : 0x00;
+	info->almost_full = (data[0] & 0x08) ? 0x01 : 0x00;
+	info->almost_empty = (data[0] & 0x04) ? 0x01 : 0x00;
+	info->overflow = (data[0] & 0x02) ? 0x01 : 0x00;
+	info->underflow = (data[0] & 0x01) ? 0x01 : 0x00;
+
+	info->packet_num = ((data[1] & 0x07) << 8) | data[2];
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
new file mode 100644
index 000000000000..a4c34f56a7a1
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
@@ -0,0 +1,52 @@
+/*
+ * cxd2880_tnrdmd_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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);
+
+int cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
+				   struct
+				   cxd2880_tnrdmd_ts_buf_info
+				   *info);
+
+#endif
-- 
2.13.0

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

* [PATCH v4 06/12] [media] cxd2880: Add integration layer for the driver
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (4 preceding siblings ...)
  2017-10-13  6:07 ` [PATCH v4 05/12] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
@ 2017-10-13  6:08 ` Yasunari.Takiguchi
  2017-12-03 18:23   ` Sean Young
  2017-12-13 19:13   ` Mauro Carvalho Chehab
  2017-10-13  6:09 ` [PATCH v4 07/12] [media] cxd2880: Add top level of " Yasunari.Takiguchi
                   ` (7 subsequent siblings)
  13 siblings, 2 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:08 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

These functions monitor the driver and watch for task completion.
This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4   
   drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
      -removed unnecessary initialization at variable declaration

Changes in V3
   drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
      -changed cxd2880_atomic_read to atomic_read
      -changed cxd2880_atomic_set to atomic_set
      -modified return code
      -modified coding style of if() 
   drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
      -modified return code

 .../media/dvb-frontends/cxd2880/cxd2880_integ.c    | 98 ++++++++++++++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_integ.h    | 44 ++++++++++
 2 files changed, 142 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
new file mode 100644
index 000000000000..7264fc355d6b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
@@ -0,0 +1,98 @@
+/*
+ * cxd2880_integ.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_integ.h"
+
+int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	int ret;
+	struct cxd2880_stopwatch timer;
+	unsigned int elapsed_time = 0;
+	u8 cpu_task_completed = 0;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_init1(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_stopwatch_start(&timer);
+	if (ret)
+		return ret;
+
+	while (1) {
+		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed_time);
+		if (ret)
+			return ret;
+
+		ret =
+		    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+						     &cpu_task_completed);
+		if (ret)
+			return ret;
+
+		if (cpu_task_completed)
+			break;
+
+		if (elapsed_time > CXD2880_TNRDMD_WAIT_INIT_TIMEOUT)
+			return -ETIME;
+		ret =
+		    cxd2880_stopwatch_sleep(&timer,
+					    CXD2880_TNRDMD_WAIT_INIT_INTVL);
+		if (ret)
+			return ret;
+	}
+
+	ret = cxd2880_tnrdmd_init2(tnr_dmd);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
new file mode 100644
index 000000000000..2b4fe5c3743b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
@@ -0,0 +1,44 @@
+/*
+ * cxd2880_integ.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
-- 
2.13.0

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

* [PATCH v4 07/12] [media] cxd2880: Add top level of the driver
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (5 preceding siblings ...)
  2017-10-13  6:08 ` [PATCH v4 06/12] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
@ 2017-10-13  6:09 ` Yasunari.Takiguchi
  2017-12-03 22:59   ` Sean Young
  2017-12-13 19:25   ` Mauro Carvalho Chehab
  2017-10-13  6:10 ` [PATCH v4 08/12] [media] cxd2880: Add DVB-T control functions " Yasunari.Takiguchi
                   ` (6 subsequent siblings)
  13 siblings, 2 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:09 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This provides the main dvb frontend operation functions
for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
      -modified typo "inavlid" to "invalid" at pr_err
      -removed unnecessary initialization at variable declaration
      -removed unnecessary brace {}
      -changed to use cxd2880_dvbt_tune and cxd2880_dvbt2_tune 
       instead of cxd2880_integ_dvbt_tune and cxd2880_integ_dvbt2_tune
       (because we changed it so that demodulator does not 
         wait for locking the signal.) 

Changes in V3
   drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
      -adjusted indent spaces
      -modified debugging code
      -removed unnecessary cast
      -modified return code
      -modified coding style of if() 
      -modified about measurement period of PER/BER.
      -changed hexadecimal code to lower case. 

 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c | 2019 +++++++++++++++++++++
 1 file changed, 2019 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
new file mode 100644
index 000000000000..c82aaf0c1597
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
@@ -0,0 +1,2019 @@
+/*
+ * cxd2880_top.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+#include <linux/spi/spi.h>
+
+#include "dvb_frontend.h"
+#include "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 -EPERM;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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 -EPERM;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+	*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 -EPERM;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	*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 -EBUSY;
+
+	if (period_exp == 11)
+		*post_bit_count = 3342336;
+	else
+		*post_bit_count = (1U << period_exp) * 204 * 81;
+
+	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 -EPERM;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	*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 -EBUSY;
+
+	n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
+
+	if (*post_bit_err > ((1U << period_exp) * n_bch))
+		return -EBUSY;
+
+	*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 -EPERM;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	*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 -EBUSY;
+
+	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 -EPERM;
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	*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 -EBUSY;
+
+	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 = (u16)(((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 = (u16)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)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if ((!fe) || (!ber)) {
+		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_pre_rsber(&priv->tnrdmd,
+							ber);
+		/* x100 to change unit.(10^7 -> 10^9) */
+		*ber *= 100;
+	} else if (c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(&priv->tnrdmd,
+							  ber);
+	} 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_set_ber_per_period_t(struct dvb_frontend *fe)
+{
+	int ret;
+	struct dtv_frontend_properties *c;
+	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;
+	c = &fe->dtv_property_cache;
+	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 dtv_frontend_properties *c;
+	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;
+	c = &fe->dtv_property_cache;
+	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 -EPERM;
+
+	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 -EOPNOTSUPP;
+	}
+
+	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);
+
+	ret = cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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 -EOPNOTSUPP;
+	}
+
+	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);
+
+	ret = cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+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)
+{
+	struct cxd2880_priv *priv = NULL;
+	int ret;
+
+	if ((!fe) || (!props)) {
+		pr_err("invalid arg.");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	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;
+
+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);
+
+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,
+};
+
+MODULE_DESCRIPTION(
+"Sony CXD2880 DVB-T2/T tuner + demodulator drvier");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
-- 
2.13.0

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

* [PATCH v4 08/12] [media] cxd2880: Add DVB-T control functions the driver
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (6 preceding siblings ...)
  2017-10-13  6:09 ` [PATCH v4 07/12] [media] cxd2880: Add top level of " Yasunari.Takiguchi
@ 2017-10-13  6:10 ` Yasunari.Takiguchi
  2017-10-13  6:11 ` [PATCH v4 09/12] [media] cxd2880: Add DVB-T monitor functions Yasunari.Takiguchi
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:10 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Provide definitions, interfaces and functions needed for DVB-T
of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
      -used over 80 columns limit, it makes fine to read codes
      -removed unnecessary initialization at variable declaration
      -removed unnecessary brace {}
      -modified how to write consecutive registers

Changes in V3
   drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
      -no change
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
      -modified return code
      -modified coding style of if() 
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
      -modified return code

 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h |  91 ++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c    | 954 +++++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h    |  62 ++
 3 files changed, 1107 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
new file mode 100644
index 000000000000..345c094760d2
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
@@ -0,0 +1,91 @@
+/*
+ * cxd2880_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T related definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
new file mode 100644
index 000000000000..c2265c4579b3
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
@@ -0,0 +1,954 @@
+/*
+ * cxd2880_tnrdmd_dvbt.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "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 -EPERM;
+	}
+
+	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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+			}
+
+			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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+			}
+
+			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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+			}
+
+			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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+			}
+
+			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 -EPERM;
+	}
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  tune_dmd_setting_seq5,
+					  ARRAY_SIZE(tune_dmd_setting_seq5));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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));
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+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;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x67,
+				     (profile == CXD2880_DVBT_PROFILE_HP)
+				     ? 0x00 : 0x01);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	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;
+	}
+
+	ret = dvbt_set_profile(tnr_dmd, tune_param->profile);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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 -EPERM;
+
+	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);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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 -EPERM;
+
+	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;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
new file mode 100644
index 000000000000..f0a36040da3b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
@@ -0,0 +1,62 @@
+/*
+ * cxd2880_tnrdmd_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
-- 
2.13.0

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

* [PATCH v4 09/12] [media] cxd2880: Add DVB-T monitor functions
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (7 preceding siblings ...)
  2017-10-13  6:10 ` [PATCH v4 08/12] [media] cxd2880: Add DVB-T control functions " Yasunari.Takiguchi
@ 2017-10-13  6:11 ` Yasunari.Takiguchi
  2017-10-13  6:13 ` [PATCH v4 10/12] [media] cxd2880: Add DVB-T2 control functions for the driver Yasunari.Takiguchi
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:11 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Provide monitor functions (DVB-T)
for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   #drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
      -cxd2880_integ_dvbt.c file was removed from V4.
   #drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
      -cxd2880_integ_dvbt.h file was removed from V4.
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
      -removed unnecessary initialization at variable declaration
      -removed unnecessary brace {}
      -changed position of static const (to top part of the file)

Changes in V3
   drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
      -changed CXD2880_SLEEP to usleep_range
      -chnaged cxd2880_atomic_set to atomic_set
      -modified return code
      -modified coding style of if() 
   drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
      -modified return code
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
      -removed unnecessary cast
      -changed cxd2880_math_log to intlog10
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
      -modified return code

 .../cxd2880/cxd2880_tnrdmd_dvbt_mon.c              | 1201 ++++++++++++++++++++
 .../cxd2880/cxd2880_tnrdmd_dvbt_mon.h              |  106 ++
 2 files changed, 1307 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
new file mode 100644
index 000000000000..b00f1ac088bb
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
@@ -0,0 +1,1201 @@
+/*
+ * cxd2880_tnrdmd_dvbt_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+
+#include "dvb_math.h"
+
+static const int nordig_non_hdvbt_db_1000[3][5] = {
+	{5100, 6900, 7900, 8900, 9700},
+	{10800, 13100, 14600, 15600, 16000},
+	{16500, 18700, 20200, 21600, 22500}
+};
+
+static const int nordig_hier_hp_dvbt_db_1000[3][2][5] = {
+	{
+		{9100, 12000, 13600, 15000, 16600},
+		{10900, 14100, 15700, 19400, 20600}
+	}, {
+		{6800, 9100, 10400, 11900, 12700},
+		{8500, 11000, 12800, 15000, 16000}
+	}, {
+		{5800, 7900, 9100, 10300, 12100},
+		{8000, 9300, 11600, 13000, 12900}
+	}
+};
+
+static const int nordig_hier_lp_dvbt_db_1000[3][2][5] = {
+	{
+		{12500, 14300, 15300, 16300, 16900},
+		{16700, 19100, 20900, 22500, 23700}
+	}, {
+		{15000, 17200, 18400, 19100, 20100},
+		{18500, 21200, 23600, 24700, 25900}
+	}, {
+		{19500, 21400, 22500, 23700, 24700},
+		{21900, 24200, 25600, 26900, 27800}
+	}
+};
+
+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 -EPERM;
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	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;
+	int ret;
+
+	if ((!tnr_dmd) || (!sync_stat) || (!unlock_detected))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd->diver_sub, sync_stat,
+					      &ts_lock_stat, unlock_detected);
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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)
+{
+	int ret;
+
+	if ((!tnr_dmd) || (!offset))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_carrier_offset(tnr_dmd->diver_sub, offset);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_pre_viterbiber(struct cxd2880_tnrdmd
+					   *tnr_dmd, u32 *ber)
+{
+	u8 rdata[2];
+	u32 bit_error = 0;
+	u32 period = 0;
+	u32 div = 0;
+	u32 Q = 0;
+	u32 R = 0;
+	int ret;
+
+	if ((!tnr_dmd) || (!ber))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	ret = slvt_freeze_reg(tnr_dmd);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x10);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x39, rdata, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if ((rdata[0] & 0x01) == 0) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EBUSY;
+	}
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x22, rdata, 2);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	bit_error = (rdata[0] << 8) | rdata[1];
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x6f, rdata, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	period = ((rdata[0] & 0x07) == 0) ? 256 : (0x1000 << (rdata[0] & 0x07));
+
+	if ((period == 0) || (bit_error > period))
+		return -EBUSY;
+
+	div = period / 128;
+
+	Q = (bit_error * 3125) / div;
+	R = (bit_error * 3125) % div;
+
+	R *= 25;
+	Q = Q * 25 + R / div;
+	R = R % div;
+
+	if (div / 2 <= R)
+		*ber = Q + 1;
+	else
+		*ber = Q;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_pre_rsber(struct cxd2880_tnrdmd
+				      *tnr_dmd, u32 *ber)
+{
+	u8 rdata[3];
+	u32 bit_error = 0;
+	u32 period_exp = 0;
+	u32 div = 0;
+	u32 Q = 0;
+	u32 R = 0;
+	int ret;
+
+	if ((!tnr_dmd) || (!ber))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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,
+				     0x15, rdata, 3);
+	if (ret)
+		return ret;
+
+	if ((rdata[0] & 0x40) == 0)
+		return -EBUSY;
+
+	bit_error = ((rdata[0] & 0x3f) << 16) | (rdata[1] << 8) | rdata[2];
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->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 -EBUSY;
+
+	if (period_exp <= 8)
+		div = (1U << period_exp) * 51;
+	else
+		div = (1U << 8) * 51;
+
+	Q = (bit_error * 250) / div;
+	R = (bit_error * 250) % div;
+
+	R *= 1250;
+	Q = Q * 1250 + R / div;
+	R = R % div;
+
+	if (period_exp > 8) {
+		*ber =
+		    (Q + (1 << (period_exp - 9))) >> (period_exp - 8);
+	} else {
+		if (div / 2 <= R)
+			*ber = Q + 1;
+		else
+			*ber = Q;
+	}
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	*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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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);
+		if (ret)
+			return ret;
+	} else {
+		int snr_main = 0;
+		int snr_sub = 0;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt_mon_snr_diver(tnr_dmd, snr, &snr_main,
+						      &snr_sub);
+		if (ret)
+			return ret;
+	}
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 == -EBUSY) {
+		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 == -EBUSY) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	ret = dvbt_calc_snr(tnr_dmd, reg_value_sum, snr);
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	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)
+{
+	int ret;
+
+	if ((!tnr_dmd) || (!ppm))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_dvbt_mon_sampling_offset(tnr_dmd->diver_sub, ppm);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_quality(struct cxd2880_tnrdmd *tnr_dmd,
+				    u8 *quality)
+{
+	struct cxd2880_dvbt_tpsinfo tps;
+	enum cxd2880_dvbt_profile profile = CXD2880_DVBT_PROFILE_HP;
+	u32 ber = 0;
+	int sn = 0;
+	int sn_rel = 0;
+	int ber_sqi = 0;
+	int ret;
+
+	if ((!tnr_dmd) || (!quality))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps);
+	if (ret)
+		return ret;
+
+	if (tps.hierarchy != CXD2880_DVBT_HIERARCHY_NON) {
+		u8 data = 0;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x10);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x67, &data, 1);
+		if (ret)
+			return ret;
+
+		profile =
+		    ((data & 0x01) ==
+		     0x01) ? CXD2880_DVBT_PROFILE_LP : CXD2880_DVBT_PROFILE_HP;
+	}
+
+	ret = cxd2880_tnrdmd_dvbt_mon_pre_rsber(tnr_dmd, &ber);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_tnrdmd_dvbt_mon_snr(tnr_dmd, &sn);
+	if (ret)
+		return ret;
+
+	if ((tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3) ||
+	    (tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5) ||
+	    (tps.rate_lp >= CXD2880_DVBT_CODERATE_RESERVED_5) ||
+	    (tps.hierarchy > CXD2880_DVBT_HIERARCHY_4)) {
+		return -EPERM;
+	}
+
+	if ((tps.hierarchy != CXD2880_DVBT_HIERARCHY_NON) &&
+	    (tps.constellation == CXD2880_DVBT_CONSTELLATION_QPSK))
+		return -EPERM;
+
+	if (tps.hierarchy == CXD2880_DVBT_HIERARCHY_NON)
+		sn_rel =
+		    sn -
+		    nordig_non_hdvbt_db_1000[tps.constellation][tps.rate_hp];
+	else if (profile == CXD2880_DVBT_PROFILE_LP)
+		sn_rel =
+		    sn - nordig_hier_lp_dvbt_db_1000[tps.hierarchy - 1]
+						    [tps.constellation - 1]
+						    [tps.rate_lp];
+	else
+		sn_rel =
+		    sn - nordig_hier_hp_dvbt_db_1000[tps.hierarchy - 1]
+						    [tps.constellation - 1]
+						    [tps.rate_hp];
+
+	if (ber > 10000) {
+		ber_sqi = 0;
+	} else if (ber > 1) {
+		ber_sqi = (int)((intlog10(ber) + 8388) / 16777);
+		ber_sqi = 20 * (7 * 1000 - (ber_sqi)) - 40 * 1000;
+	} else {
+		ber_sqi = 100 * 1000;
+	}
+
+	if (sn_rel < -7 * 1000) {
+		*quality = 0;
+	} else if (sn_rel < 3 * 1000) {
+		int tmp_sqi = (((sn_rel - (3 * 1000)) / 10) + 1000);
+		*quality =
+		    (u8)(((tmp_sqi * ber_sqi) +
+			   (1000000 / 2)) / (1000000)) & 0xff;
+	} else {
+		*quality = (u8)((ber_sqi + 500) / 1000);
+	}
+
+	if (*quality > 100)
+		*quality = 100;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+				u32 *per)
+{
+	u32 packet_error = 0;
+	u32 period = 0;
+	u8 rdata[3];
+	u32 div = 0;
+	u32 Q = 0;
+	u32 R = 0;
+	int ret;
+
+	if ((!tnr_dmd) || (!per))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	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,
+				     0x18, rdata, 3);
+	if (ret)
+		return ret;
+
+	if ((rdata[0] & 0x01) == 0)
+		return -EBUSY;
+
+	packet_error = (rdata[1] << 8) | rdata[2];
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x5c, rdata, 1);
+	if (ret)
+		return ret;
+
+	period = 1U << (rdata[0] & 0x0f);
+
+	if ((period == 0) || (packet_error > period))
+		return -EBUSY;
+
+	div = period;
+
+	Q = (packet_error * 1000) / div;
+	R = (packet_error * 1000) % div;
+
+	R *= 1000;
+	Q = Q * 1000 + R / div;
+	R = R % div;
+
+	if ((div != 1) && (div / 2 <= R))
+		*per = Q + 1;
+	else
+		*per = Q;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+	if (ret)
+		return ret;
+
+	ret = dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EPERM;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+	if (ret)
+		return ret;
+
+	ret = dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+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 -EBUSY;
+
+	return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
new file mode 100644
index 000000000000..ce966270c69b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
@@ -0,0 +1,106 @@
+/*
+ * cxd2880_tnrdmd_dvbt_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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_pre_viterbiber(struct cxd2880_tnrdmd
+					   *tnr_dmd, u32 *ber);
+
+int cxd2880_tnrdmd_dvbt_mon_pre_rsber(struct cxd2880_tnrdmd
+				      *tnr_dmd, u32 *ber);
+
+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_quality(struct cxd2880_tnrdmd *tnr_dmd,
+				    u8 *quality);
+
+int cxd2880_tnrdmd_dvbt_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+				u32 *per);
+
+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
-- 
2.13.0

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

* [PATCH v4 10/12] [media] cxd2880: Add DVB-T2 control functions for the driver
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (8 preceding siblings ...)
  2017-10-13  6:11 ` [PATCH v4 09/12] [media] cxd2880: Add DVB-T monitor functions Yasunari.Takiguchi
@ 2017-10-13  6:13 ` Yasunari.Takiguchi
  2017-10-13  6:15 ` [PATCH v4 11/12] [media] cxd2880: Add DVB-T2 monitor functions Yasunari.Takiguchi
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:13 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Provide definitions, interfaces and functions needed for DVB-T2
of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
      -removed unnecessary initialization at variable declaration
      -removed unnecessary brace {}
      -modified how to write consecutive registers

Changes in V3
   drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
      -modified return code
      -modified coding style of if() 
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
      -modified return code

 .../media/dvb-frontends/cxd2880/cxd2880_dvbt2.h    |  402 +++++++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c   | 1260 ++++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h   |   82 ++
 3 files changed, 1744 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
new file mode 100644
index 000000000000..674ed17deef5
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
@@ -0,0 +1,402 @@
+/*
+ * cxd2880_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 related definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
new file mode 100644
index 000000000000..76ca6b8a765a
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
@@ -0,0 +1,1260 @@
+/*
+ * cxd2880_tnrdmd_dvbt2.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "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 -EPERM;
+	}
+
+	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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+			}
+
+			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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+			}
+
+			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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+			}
+
+			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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+			}
+
+			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 -EPERM;
+		}
+
+		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 -EPERM;
+		}
+
+		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 -EPERM;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	default:
+		return -EPERM;
+	}
+
+	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,
+				     0xfd, 0x01);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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;
+
+	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);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+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 -EPERM;
+	}
+
+	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;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x2c, seq_not2_dtime);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if ((tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) &&
+	    (tune_param->profile == CXD2880_DVBT2_PROFILE_ANY))
+		return -EOPNOTSUPP;
+
+	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);
+		if (ret)
+			return ret;
+	} else {
+		ret =
+		    cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 0,
+					     (u8)(tune_param->data_plp_id));
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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 -EPERM;
+
+	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);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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 -EPERM;
+
+	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 -EPERM;
+
+	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;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xad, auto_plp ? 0x00 : 0x01);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	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;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x47, data, 12);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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 -EPERM;
+
+	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;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
new file mode 100644
index 000000000000..409685425b53
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
@@ -0,0 +1,82 @@
+/*
+ * cxd2880_tnrdmd_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
-- 
2.13.0

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

* [PATCH v4 11/12] [media] cxd2880: Add DVB-T2 monitor functions
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (9 preceding siblings ...)
  2017-10-13  6:13 ` [PATCH v4 10/12] [media] cxd2880: Add DVB-T2 control functions for the driver Yasunari.Takiguchi
@ 2017-10-13  6:15 ` Yasunari.Takiguchi
  2017-10-13  6:17 ` [PATCH v4 12/12] [media] cxd2880: Add all Makefile, Kconfig files and Update MAINTAINERS file for the driver Yasunari.Takiguchi
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:15 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Provide monitor functions (DVB-T2)
for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   #drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
      -cxd2880_integ_dvbt2.c file was removed from V4.
   #drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
      -cxd2880_integ_dvbt2.h file was removed from V4.
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
      -removed unnecessary initialization at variable declaration
      -removed unnecessary brace {}
      -changed position of static const (to top part of the file)

Changes in V3
   drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
      -changed CXD2880_SLEEP to usleep_range
      -replaced cxd2880_atomic_set to atomic_set
      -modified return code
      -modified coding style of if()  
   drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
      -modified return code
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
      -removed unnecessary cast
      -changed cxd2880_math_log to intlog10
      -modified return code
      -modified coding style of if() 
      -changed hexadecimal code to lower case. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
      -modified return code

 .../cxd2880/cxd2880_tnrdmd_dvbt2_mon.c             | 2535 ++++++++++++++++++++
 .../cxd2880/cxd2880_tnrdmd_dvbt2_mon.h             |  170 ++
 2 files changed, 2705 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
new file mode 100644
index 000000000000..dac92243e907
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
@@ -0,0 +1,2535 @@
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+
+#include "dvb_math.h"
+
+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}
+};
+
+static const int snr_nordig_p1_db_1000[4][8] = {
+	{3500, 4700, 5600, 6600, 7200, 7700, 1300, 2200},
+	{8700, 10100, 11400, 12500, 13300, 13800, 6000, 7200},
+	{13000, 14800, 16200, 17700, 18700, 19400, 9800, 11100},
+	{17000, 19400, 20800, 22900, 24300, 25100, 13200, 14800},
+};
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	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;
+
+	int ret;
+
+	if ((!tnr_dmd) || (!sync_stat) || (!unlock_detected))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd->diver_sub, sync_stat,
+					       &ts_lock_stat, unlock_detected);
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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 -EPERM;
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct
+						cxd2880_tnrdmd
+						*tnr_dmd,
+						int *offset)
+{
+	int ret;
+
+	if ((!tnr_dmd) || (!offset))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_carrier_offset(tnr_dmd->diver_sub, offset);
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+			}
+		} else {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EBUSY;
+		}
+	}
+
+	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 -EBUSY;
+		}
+	} 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 -EBUSY;
+		}
+	} else {
+		return -EBUSY;
+	}
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+			}
+		} else {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EBUSY;
+		}
+	}
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 = -EBUSY;
+
+		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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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 -EPERM;
+	}
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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] == 0))
+		return -EBUSY;
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+			}
+		} else {
+			slvt_unfreeze_reg(tnr_dmd);
+			return -EBUSY;
+		}
+	}
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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 -EBUSY;
+		}
+
+		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 -EBUSY;
+		}
+	}
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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 -EBUSY;
+	}
+
+	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 -EBUSY;
+	}
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 = -EBUSY;
+
+		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 -EBUSY;
+	}
+
+	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 -EBUSY;
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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);
+		if (ret)
+			return ret;
+	} else {
+		int snr_main = 0;
+		int snr_sub = 0;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_snr_diver(tnr_dmd, snr, &snr_main,
+						       &snr_sub);
+		if (ret)
+			return ret;
+	}
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 == -EBUSY) {
+		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 == -EBUSY) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	ret = dvbt2_calc_snr(tnr_dmd, reg_value_sum, snr);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_pre_ldpcber(struct cxd2880_tnrdmd
+					 *tnr_dmd, u32 *ber)
+{
+	u32 bit_error = 0;
+	u32 period_exp = 0;
+	u32 n_ldpc = 0;
+	u8 data[5];
+	u32 div = 0;
+	u32 Q = 0;
+	u32 R = 0;
+	int ret;
+
+	if ((!tnr_dmd) || (!ber))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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,
+				     0x3c, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (!(data[0] & 0x01)) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EBUSY;
+	}
+
+	bit_error =
+	    ((data[1] & 0x0f) << 24) | (data[2] << 16) | (data[3] << 8)
+	    | data[4];
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xa0, data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		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(tnr_dmd);
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x20);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x6f, data, 1);
+	if (ret)
+		return ret;
+
+	period_exp = data[0] & 0x0f;
+
+	if (bit_error > ((1U << period_exp) * n_ldpc))
+		return -EBUSY;
+
+	if (period_exp >= 4) {
+		div = (1U << (period_exp - 4)) * (n_ldpc / 200);
+
+		Q = (bit_error * 5) / div;
+		R = (bit_error * 5) % div;
+
+		R *= 625;
+		Q = Q * 625 + R / div;
+		R = R % div;
+	} else {
+		div = (1U << period_exp) * (n_ldpc / 200);
+
+		Q = (bit_error * 10) / div;
+		R = (bit_error * 10) % div;
+
+		R *= 5000;
+		Q = Q * 5000 + R / div;
+		R = R % div;
+	}
+
+	if (div / 2 <= R)
+		*ber = Q + 1;
+	else
+		*ber = Q;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_post_bchfer(struct cxd2880_tnrdmd
+					 *tnr_dmd, u32 *fer)
+{
+	u32 fec_error = 0;
+	u32 period = 0;
+	u8 data[2];
+	u32 div = 0;
+	u32 Q = 0;
+	u32 R = 0;
+	int ret;
+
+	if ((!tnr_dmd) || (!fer))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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,
+				     0x1b, data, 2);
+	if (ret)
+		return ret;
+
+	if (!(data[0] & 0x80))
+		return -EBUSY;
+
+	fec_error = ((data[0] & 0x7f) << 8) | (data[1]);
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x20);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x72, data, 1);
+	if (ret)
+		return ret;
+
+	period = (1 << (data[0] & 0x0f));
+
+	if ((period == 0) || (fec_error > period))
+		return -EBUSY;
+
+	div = period;
+
+	Q = (fec_error * 1000) / div;
+	R = (fec_error * 1000) % div;
+
+	R *= 1000;
+	Q = Q * 1000 + R / div;
+	R = R % div;
+
+	if ((div != 1) && (div / 2 <= R))
+		*fer = Q + 1;
+	else
+		*fer = Q;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_pre_bchber(struct cxd2880_tnrdmd
+					*tnr_dmd, u32 *ber)
+{
+	u32 bit_error = 0;
+	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_cr = CXD2880_DVBT2_R1_2;
+	u32 div = 0;
+	u32 Q = 0;
+	u32 R = 0;
+	int ret;
+
+	if ((!tnr_dmd) || (!ber))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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,
+				     0x15, data, 3);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (!(data[0] & 0x40)) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EBUSY;
+	}
+
+	bit_error = ((data[0] & 0x3f) << 16) | (data[1] << 8) | data[2];
+
+	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;
+	}
+
+	plp_cr = (enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xa0, data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x20);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->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_cr > CXD2880_DVBT2_R2_5))
+		return -EBUSY;
+
+	n_bch = n_bch_bits_lookup[plp_fec_type][plp_cr];
+
+	if (bit_error > ((1U << period_exp) * n_bch))
+		return -EBUSY;
+
+	if (period_exp >= 6) {
+		div = (1U << (period_exp - 6)) * (n_bch / 40);
+
+		Q = (bit_error * 625) / div;
+		R = (bit_error * 625) % div;
+
+		R *= 625;
+		Q = Q * 625 + R / div;
+		R = R % div;
+	} else {
+		div = (1U << period_exp) * (n_bch / 40);
+
+		Q = (bit_error * 1000) / div;
+		R = (bit_error * 1000) % div;
+
+		R *= 25000;
+		Q = Q * 25000 + R / div;
+		R = R % div;
+	}
+
+	if (div / 2 <= R)
+		*ber = Q + 1;
+	else
+		*ber = Q;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+
+	*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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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 -EBUSY;
+
+	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)
+{
+	int ret;
+
+	if ((!tnr_dmd) || (!ppm))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_sampling_offset(tnr_dmd->diver_sub, ppm);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_quality(struct cxd2880_tnrdmd
+				     *tnr_dmd, u8 *quality)
+{
+	int ret;
+	int snr = 0;
+	int snr_rel = 0;
+	u32 ber = 0;
+	u32 ber_sqi = 0;
+	enum cxd2880_dvbt2_plp_constell qam;
+	enum cxd2880_dvbt2_plp_code_rate code_rate;
+
+	if ((!tnr_dmd) || (!quality))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(tnr_dmd, &ber);
+	if (ret)
+		return ret;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_snr(tnr_dmd, &snr);
+	if (ret)
+		return ret;
+
+	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 -EPERM;
+
+	if (ber > 100000)
+		ber_sqi = 0;
+	else if (ber >= 100)
+		ber_sqi = 6667;
+	else
+		ber_sqi = 16667;
+
+	snr_rel = snr - snr_nordig_p1_db_1000[qam][code_rate];
+
+	if (snr_rel < -3000) {
+		*quality = 0;
+	} else if (snr_rel <= 3000) {
+		u32 temp_sqi =
+		    (((snr_rel + 3000) * ber_sqi) + 500000) / 1000000;
+		*quality = (temp_sqi > 100) ? 100 : (u8)temp_sqi;
+	} else {
+		*quality = 100;
+	}
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_ts_rate(struct cxd2880_tnrdmd
+				     *tnr_dmd, u32 *ts_rate_kbps)
+{
+	int ret;
+	u32 rd_smooth_dp = 0;
+	u32 ep_ck_nume = 0;
+	u32 ep_ck_deno = 0;
+	u8 issy_on_data = 0;
+	u8 data[12];
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u32 ick_x100;
+	u32 div = 0;
+	u32 Q = 0;
+	u32 R = 0;
+
+	if ((!tnr_dmd) || (!ts_rate_kbps))
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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,
+				     0x23, data, 12);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	rd_smooth_dp = (data[0] & 0x1f) << 24;
+	rd_smooth_dp |= data[1] << 16;
+	rd_smooth_dp |= data[2] << 8;
+	rd_smooth_dp |= data[3];
+
+	if (rd_smooth_dp < 214958) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return -EBUSY;
+	}
+
+	ep_ck_nume = (data[4] & 0x3f) << 24;
+	ep_ck_nume |= data[5] << 16;
+	ep_ck_nume |= data[6] << 8;
+	ep_ck_nume |= data[7];
+
+	ep_ck_deno = (data[8] & 0x3f) << 24;
+	ep_ck_deno |= data[9] << 16;
+	ep_ck_deno |= data[10] << 8;
+	ep_ck_deno |= data[11];
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x41, data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	issy_on_data = data[0] & 0x01;
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	if (issy_on_data) {
+		if ((ep_ck_deno == 0) || (ep_ck_nume == 0) ||
+		    (ep_ck_deno >= ep_ck_nume))
+			return -EBUSY;
+	}
+
+	switch (tnr_dmd->clk_mode) {
+	case CXD2880_TNRDMD_CLOCKMODE_A:
+		ick_x100 = 8228;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_B:
+		ick_x100 = 9330;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_C:
+		ick_x100 = 9600;
+		break;
+	default:
+		return -EPERM;
+	}
+
+	div = rd_smooth_dp;
+
+	Q = ick_x100 * 262144U / div;
+	R = ick_x100 * 262144U % div;
+
+	R *= 5U;
+	Q = Q * 5 + R / div;
+	R = R % div;
+
+	R *= 2U;
+	Q = Q * 2 + R / div;
+	R = R % div;
+
+	if (div / 2 <= R)
+		*ts_rate_kbps = Q + 1;
+	else
+		*ts_rate_kbps = Q;
+
+	if (issy_on_data) {
+		u32 diff = ep_ck_nume - ep_ck_deno;
+
+		while (diff > 0x7fff) {
+			diff >>= 1;
+			ep_ck_nume >>= 1;
+		}
+
+		*ts_rate_kbps -=
+		    (*ts_rate_kbps * diff + ep_ck_nume / 2) / ep_ck_nume;
+	}
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+				 u32 *per)
+{
+	u32 packet_error = 0;
+	u32 period = 0;
+	u8 rdata[3];
+	u32 div = 0;
+	u32 Q = 0;
+	u32 R = 0;
+	int ret;
+
+	if (!tnr_dmd || !per)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EPERM;
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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,
+				     0x18, rdata, 3);
+	if (ret)
+		return ret;
+
+	if ((rdata[0] & 0x01) == 0)
+		return -EBUSY;
+
+	packet_error = (rdata[1] << 8) | rdata[2];
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x24);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xdc, rdata, 1);
+	if (ret)
+		return ret;
+
+	period = 1U << (rdata[0] & 0x0f);
+
+	if ((period == 0) || (packet_error > period))
+		return -EBUSY;
+
+	div = period;
+
+	Q = (packet_error * 1000) / div;
+	R = (packet_error * 1000) % div;
+
+	R *= 1000;
+	Q = Q * 1000 + R / div;
+	R = R % div;
+
+	if ((div != 1) && (div / 2 <= R))
+		*per = Q + 1;
+	else
+		*per = Q;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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 -EBUSY;
+		}
+
+		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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 -EBUSY;
+	}
+
+	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 -EBUSY;
+		}
+
+		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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	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 = -EBUSY;
+		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 -EPERM;
+
+	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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+	if (ret)
+		return ret;
+
+	ret = dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+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 -EPERM;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EPERM;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+	if (ret)
+		return ret;
+
+	ret = dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi);
+	if (ret)
+		return ret;
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
new file mode 100644
index 000000000000..f7b2f618e80f
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
@@ -0,0 +1,170 @@
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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_pre_ldpcber(struct cxd2880_tnrdmd
+					 *tnr_dmd, u32 *ber);
+
+int cxd2880_tnrdmd_dvbt2_mon_post_bchfer(struct cxd2880_tnrdmd
+					 *tnr_dmd, u32 *fer);
+
+int cxd2880_tnrdmd_dvbt2_mon_pre_bchber(struct cxd2880_tnrdmd
+					*tnr_dmd, u32 *ber);
+
+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_ts_rate(struct cxd2880_tnrdmd
+				     *tnr_dmd, u32 *ts_rate_kbps);
+
+int cxd2880_tnrdmd_dvbt2_mon_quality(struct cxd2880_tnrdmd
+				     *tnr_dmd, u8 *quality);
+
+int cxd2880_tnrdmd_dvbt2_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+				 u32 *per);
+
+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
-- 
2.13.0

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

* [PATCH v4 12/12] [media] cxd2880: Add all Makefile, Kconfig files and Update MAINTAINERS file for the driver
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (10 preceding siblings ...)
  2017-10-13  6:15 ` [PATCH v4 11/12] [media] cxd2880: Add DVB-T2 monitor functions Yasunari.Takiguchi
@ 2017-10-13  6:17 ` Yasunari.Takiguchi
  2017-11-16  1:07 ` [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Takiguchi, Yasunari
  2017-12-13 19:36 ` Mauro Carvalho Chehab
  13 siblings, 0 replies; 43+ messages in thread
From: Yasunari.Takiguchi @ 2017-10-13  6:17 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This is the Makefile, Kconfig files of driver 
and MAINTAINERS file update about the driver 
for the Sony CXD2880 DVB-T2/T tuner + demodulator.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---

[Change list]
Changes in V4
   We put [PATCH v3 12/14], [PATCH v3 13/14] and [PATCH v3 14/14]
   in [PATCH v4 12/12].
   
   drivers/media/dvb-frontends/cxd2880/Makefile
      -removed cxd2880_integ_dvbt2.o and cxd2880_integ_dvbt.o 

Changes in V3
   drivers/media/dvb-frontends/cxd2880/Makefile
      -removed cxd2880_math.o  

 MAINTAINERS                                  |  9 +++++++++
 drivers/media/dvb-frontends/Kconfig          |  2 ++
 drivers/media/dvb-frontends/Makefile         |  1 +
 drivers/media/dvb-frontends/cxd2880/Kconfig  |  6 ++++++
 drivers/media/dvb-frontends/cxd2880/Makefile | 18 ++++++++++++++++++
 drivers/media/spi/Kconfig                    | 14 ++++++++++++++
 drivers/media/spi/Makefile                   |  5 +++++
 7 files changed, 55 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/Kconfig
 create mode 100644 drivers/media/dvb-frontends/cxd2880/Makefile

diff --git a/MAINTAINERS b/MAINTAINERS
index a74227ad082e..9451def42ebe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8447,6 +8447,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
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index 2631d0e0a024..f29100832639 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -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
 
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index f45f6a4a4371..e6b387284f6f 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -129,3 +129,4 @@ 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_CXD2880) += cxd2880/
diff --git a/drivers/media/dvb-frontends/cxd2880/Kconfig b/drivers/media/dvb-frontends/cxd2880/Kconfig
new file mode 100644
index 000000000000..36b8b6f7c4f7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Kconfig
@@ -0,0 +1,6 @@
+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.
\ No newline at end of file
diff --git a/drivers/media/dvb-frontends/cxd2880/Makefile b/drivers/media/dvb-frontends/cxd2880/Makefile
new file mode 100644
index 000000000000..dddedd178cf7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Makefile
@@ -0,0 +1,18 @@
+cxd2880-objs := cxd2880_common.o \
+		cxd2880_devio_spi.o \
+		cxd2880_integ.o \
+		cxd2880_io.o \
+		cxd2880_spi_device.o \
+		cxd2880_stopwatch_port.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-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig
index a21f5a39a440..b07ac86fc53c 100644
--- a/drivers/media/spi/Kconfig
+++ b/drivers/media/spi/Kconfig
@@ -12,3 +12,17 @@ config VIDEO_GS1662
 endmenu
 
 endif
+
+if SPI
+menu "Media SPI Adapters"
+
+config CXD2880_SPI_DRV
+	tristate "Sony CXD2880 SPI support"
+	depends on DVB_CORE && SPI
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Choose if you would like to have SPI interface support for Sony CXD2880.
+
+endmenu
+
+endif
diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile
index ea64013d16cc..40e0f88d9f6c 100644
--- a/drivers/media/spi/Makefile
+++ b/drivers/media/spi/Makefile
@@ -1 +1,6 @@
 obj-$(CONFIG_VIDEO_GS1662) += gs1662.o
+obj-$(CONFIG_CXD2880_SPI_DRV) += cxd2880-spi.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/dvb-frontends/cxd2880
\ No newline at end of file
-- 
2.13.0

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

* Re: [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2017-10-13  5:59 ` [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Yasunari.Takiguchi
@ 2017-10-13  8:44   ` Honza Petrouš
  2017-10-13  9:10     ` Takiguchi, Yasunari
  2017-12-13 17:54   ` Mauro Carvalho Chehab
  1 sibling, 1 reply; 43+ messages in thread
From: Honza Petrouš @ 2017-10-13  8:44 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Hi Yasunari

2017-10-13 7:59 GMT+02:00  <Yasunari.Takiguchi@sony.com>:
> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
>
> This is the SPI adapter part of the driver for the
> Sony CXD2880 DVB-T2/T tuner + demodulator.
>

[...]

> +static const struct of_device_id cxd2880_spi_of_match[] = {
> +       { .compatible = "sony,cxd2880" },
> +       { /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match);
> +
> +static int
> +cxd2880_spi_probe(struct spi_device *spi)
> +{
> +       int ret;
> +       struct cxd2880_dvb_spi *dvb_spi = NULL;
> +       struct cxd2880_config config;
> +
> +       if (!spi) {
> +               pr_err("invalid arg.\n");
> +               return -EINVAL;
> +       }
> +
> +       dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL);
> +       if (!dvb_spi)
> +               return -ENOMEM;
> +
> +       dvb_spi->spi = spi;
> +       mutex_init(&dvb_spi->spi_mutex);
> +       dev_set_drvdata(&spi->dev, dvb_spi);
> +       config.spi = spi;
> +       config.spi_mutex = &dvb_spi->spi_mutex;
> +
> +       ret = dvb_register_adapter(&dvb_spi->adapter,
> +                                  "CXD2880",
> +                                  THIS_MODULE,
> +                                  &spi->dev,
> +                                  adapter_nr);
> +       if (ret < 0) {
> +               pr_err("dvb_register_adapter() failed\n");
> +               goto fail_adapter;
> +       }
> +
> +       if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
> +               pr_err("cxd2880_attach failed\n");
> +               goto fail_attach;
> +       }
> +
> +       ret = dvb_register_frontend(&dvb_spi->adapter,
> +                                   &dvb_spi->dvb_fe);
> +       if (ret < 0) {
> +               pr_err("dvb_register_frontend() failed\n");
> +               goto fail_frontend;
> +       }
> +
> +       dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING;
> +       dvb_spi->demux.priv = dvb_spi;
> +       dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE;
> +       dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE;
> +       dvb_spi->demux.start_feed = cxd2880_start_feed;
> +       dvb_spi->demux.stop_feed = cxd2880_stop_feed;
> +
> +       ret = dvb_dmx_init(&dvb_spi->demux);
> +       if (ret < 0) {
> +               pr_err("dvb_dmx_init() failed\n");
> +               goto fail_dmx;
> +       }
> +
> +       dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE;
> +       dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx;
> +       dvb_spi->dmxdev.capabilities = 0;
> +       ret = dvb_dmxdev_init(&dvb_spi->dmxdev,
> +                             &dvb_spi->adapter);
> +       if (ret < 0) {
> +               pr_err("dvb_dmxdev_init() failed\n");
> +               goto fail_dmxdev;
> +       }
> +
> +       dvb_spi->dmx_fe.source = DMX_FRONTEND_0;
> +       ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx,
> +                                             &dvb_spi->dmx_fe);
> +       if (ret < 0) {
> +               pr_err("add_frontend() failed\n");
> +               goto fail_dmx_fe;
> +       }
> +
> +       ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx,
> +                                                 &dvb_spi->dmx_fe);
> +       if (ret < 0) {
> +               pr_err("dvb_register_frontend() failed\n");
> +               goto fail_fe_conn;
> +       }
> +
> +       pr_info("Sony CXD2880 has successfully attached.\n");
> +
> +       return 0;
> +
> +fail_fe_conn:
> +       dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
> +                                          &dvb_spi->dmx_fe);
> +fail_dmx_fe:
> +       dvb_dmxdev_release(&dvb_spi->dmxdev);
> +fail_dmxdev:
> +       dvb_dmx_release(&dvb_spi->demux);
> +fail_dmx:
> +       dvb_unregister_frontend(&dvb_spi->dvb_fe);
> +fail_frontend:
> +       dvb_frontend_detach(&dvb_spi->dvb_fe);
> +fail_attach:
> +       dvb_unregister_adapter(&dvb_spi->adapter);
> +fail_adapter:
> +       kfree(dvb_spi);
> +       return ret;
> +}
> +
> +static int
> +cxd2880_spi_remove(struct spi_device *spi)
> +{
> +       struct cxd2880_dvb_spi *dvb_spi;
> +
> +       if (!spi) {
> +               pr_err("invalid arg\n");
> +               return -EINVAL;
> +       }
> +
> +       dvb_spi = dev_get_drvdata(&spi->dev);
> +
> +       if (!dvb_spi) {
> +               pr_err("failed\n");
> +               return -EINVAL;
> +       }
> +       dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
> +                                          &dvb_spi->dmx_fe);
> +       dvb_dmxdev_release(&dvb_spi->dmxdev);
> +       dvb_dmx_release(&dvb_spi->demux);
> +       dvb_unregister_frontend(&dvb_spi->dvb_fe);
> +       dvb_frontend_detach(&dvb_spi->dvb_fe);
> +       dvb_unregister_adapter(&dvb_spi->adapter);
> +
> +       kfree(dvb_spi);
> +       pr_info("cxd2880_spi remove ok.\n");
> +
> +       return 0;
> +}
> +
> +static const struct spi_device_id cxd2880_spi_id[] = {
> +       { "cxd2880", 0 },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(spi, cxd2880_spi_id);
> +
> +static struct spi_driver cxd2880_spi_driver = {
> +       .driver = {
> +               .name   = "cxd2880",
> +               .of_match_table = cxd2880_spi_of_match,
> +       },
> +       .id_table = cxd2880_spi_id,
> +       .probe    = cxd2880_spi_probe,
> +       .remove   = cxd2880_spi_remove,
> +};
> +module_spi_driver(cxd2880_spi_driver);
> +
> +MODULE_DESCRIPTION(
> +"Sony CXD2880 DVB-T2/T tuner + demodulator drvier SPI adapter");
> +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
> +MODULE_LICENSE("GPL v2");
> --
> 2.13.0
>

It looks like very interesting device!

If I understand it right, it uses SPI bus also for passing transport stream
to the host system (also having some pid piltering inside!), isn't it?

It would be interesting to know what is the max throughput of the CXD's SPI?

/Honza

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

* Re: [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2017-10-13  8:44   ` Honza Petrouš
@ 2017-10-13  9:10     ` Takiguchi, Yasunari
  2017-10-13 10:26       ` Honza Petrouš
  0 siblings, 1 reply; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-10-13  9:10 UTC (permalink / raw)
  To: Honza Petrouš
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

Hi Honza

> If I understand it right, it uses SPI bus also for passing transport stream
> to the host system (also having some pid piltering inside!), isn't it?
> It would be interesting to know what is the max throughput of the CXD's SPI?

Yes, max clock rate of spi is written in sony-cxd2880.txt of [patch v4 01/12].

  spi-max-frequency = <55000000>; /* 55MHz */

Takiguchi 

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

* Re: [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2017-10-13  9:10     ` Takiguchi, Yasunari
@ 2017-10-13 10:26       ` Honza Petrouš
  2017-10-16  1:35         ` Takiguchi, Yasunari
  0 siblings, 1 reply; 43+ messages in thread
From: Honza Petrouš @ 2017-10-13 10:26 UTC (permalink / raw)
  To: Takiguchi, Yasunari
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

Hi Takiguchi,

>> If I understand it right, it uses SPI bus also for passing transport stream
>> to the host system (also having some pid piltering inside!), isn't it?
>> It would be interesting to know what is the max throughput of the CXD's SPI?
>
> Yes, max clock rate of spi is written in sony-cxd2880.txt of [patch v4 01/12].
>
>   spi-max-frequency = <55000000>; /* 55MHz */

Ehh, I overlooked this! Thanks for pointing me out.

BTW, is there any device, preferable some devboard with this silicon
on the market?

/Honza

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

* Re: [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2017-10-13 10:26       ` Honza Petrouš
@ 2017-10-16  1:35         ` Takiguchi, Yasunari
  0 siblings, 0 replies; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-10-16  1:35 UTC (permalink / raw)
  To: Honza Petrouš
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

Hi Honza,

> BTW, is there any device, preferable some devboard with this silicon
> on the market?
There is not devboard at this moment, but we are promoting this device.

Takiguchi

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

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (11 preceding siblings ...)
  2017-10-13  6:17 ` [PATCH v4 12/12] [media] cxd2880: Add all Makefile, Kconfig files and Update MAINTAINERS file for the driver Yasunari.Takiguchi
@ 2017-11-16  1:07 ` Takiguchi, Yasunari
  2017-11-22  4:17   ` Takiguchi, Yasunari
  2017-12-13 19:36 ` Mauro Carvalho Chehab
  13 siblings, 1 reply; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-11-16  1:07 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

Hi, all

I sent the patch series of Sony CXD2880 DVB-T2/T tuner + demodulator driver version 4 on 13th/Oct.
Are there any comments, advices and review results for them?

I'd like to get better understanding of current review status for our codes.

Regards,
Takiguchi

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

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-11-16  1:07 ` [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Takiguchi, Yasunari
@ 2017-11-22  4:17   ` Takiguchi, Yasunari
  2017-11-29  7:38     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-11-22  4:17 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media, Mauro Carvalho Chehab
  Cc: tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

Hi, all

I sent the patch series of Sony CXD2880 DVB-T2/T tuner + demodulator driver version 4 on 13th/Oct.
I'd like to get better understanding of current review status for our codes.

Are there any comments, advices and review results for them?

Regards,
Takiguchi

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

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-11-22  4:17   ` Takiguchi, Yasunari
@ 2017-11-29  7:38     ` Mauro Carvalho Chehab
  2017-11-29  8:21       ` Takiguchi, Yasunari
  0 siblings, 1 reply; 43+ messages in thread
From: Mauro Carvalho Chehab @ 2017-11-29  7:38 UTC (permalink / raw)
  To: Takiguchi, Yasunari
  Cc: akpm, linux-kernel, devicetree, linux-media, tbird20d,
	frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Sean Young, Michael Ira Krufky, Bird, Timothy

Em Wed, 22 Nov 2017 13:17:14 +0900
"Takiguchi, Yasunari" <Yasunari.Takiguchi@sony.com> escreveu:

Hi Takiguchi-san,

> Hi, all
> 
> I sent the patch series of Sony CXD2880 DVB-T2/T tuner + demodulator driver version 4 on 13th/Oct.
> I'd like to get better understanding of current review status for our codes.
> 
> Are there any comments, advices and review results for them?

October was a month crowded of trips for everybody. I had some trips in
November too, and a merge window to take care of, with ended by delaying
patch reviews. We didn't even had the time yet to finish the summit report.
Anyway, now Sean and Michael are helping with DVB patch review. 

Michael/Sean, could you please take a look on this patch series?

I'll try to take a look on it myself next week.

Thanks,
Mauro

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

* RE: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-11-29  7:38     ` Mauro Carvalho Chehab
@ 2017-11-29  8:21       ` Takiguchi, Yasunari
  0 siblings, 0 replies; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-11-29  8:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: akpm, linux-kernel, devicetree, linux-media, tbird20d,
	frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Sean Young, Michael Ira Krufky, Bird, Timothy, Takiguchi,
	Yasunari

Dear Mauro

Thanks for your support and reply.
I understood current status.

We will wait for community's feedback.

Regards and Thanks,
Takiguchi

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

* Re: [PATCH v4 06/12] [media] cxd2880: Add integration layer for the driver
  2017-10-13  6:08 ` [PATCH v4 06/12] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
@ 2017-12-03 18:23   ` Sean Young
  2017-12-13 19:13   ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 43+ messages in thread
From: Sean Young @ 2017-12-03 18:23 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

On Fri, Oct 13, 2017 at 03:08:34PM +0900, Yasunari.Takiguchi@sony.com wrote:
> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> These functions monitor the driver and watch for task completion.
> This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
> 
> [Change list]
> Changes in V4   
>    drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
>       -removed unnecessary initialization at variable declaration
> 
> Changes in V3
>    drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
>       -changed cxd2880_atomic_read to atomic_read
>       -changed cxd2880_atomic_set to atomic_set
>       -modified return code
>       -modified coding style of if() 
>    drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
>       -modified return code
> 
>  .../media/dvb-frontends/cxd2880/cxd2880_integ.c    | 98 ++++++++++++++++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_integ.h    | 44 ++++++++++
>  2 files changed, 142 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
> new file mode 100644
> index 000000000000..7264fc355d6b
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
> @@ -0,0 +1,98 @@
> +/*
> + * cxd2880_integ.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * integration layer common functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_tnrdmd.h"
> +#include "cxd2880_tnrdmd_mon.h"
> +#include "cxd2880_integ.h"
> +
> +int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	int ret;
> +	struct cxd2880_stopwatch timer;
> +	unsigned int elapsed_time = 0;
> +	u8 cpu_task_completed = 0;
> +
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	ret = cxd2880_tnrdmd_init1(tnr_dmd);
> +	if (ret)
> +		return ret;
> +
> +	ret = cxd2880_stopwatch_start(&timer);
> +	if (ret)
> +		return ret;
> +
> +	while (1) {
> +		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed_time);
> +		if (ret)
> +			return ret;
> +
> +		ret =
> +		    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
> +						     &cpu_task_completed);
> +		if (ret)
> +			return ret;
> +
> +		if (cpu_task_completed)
> +			break;
> +
> +		if (elapsed_time > CXD2880_TNRDMD_WAIT_INIT_TIMEOUT)
> +			return -ETIME;

ETIMEOUT?

> +		ret =
> +		    cxd2880_stopwatch_sleep(&timer,
> +					    CXD2880_TNRDMD_WAIT_INIT_INTVL);
> +		if (ret)
> +			return ret;
> +	}

This could be simplified. Also, I'm worried that the jiffies code in
cxd2880_stopwatch_port.c does not deal with jiffies wrapping correctly; working 
with ktime easier in that regard.

{
	ktime_t start = ktime_get();

	for (;;) {
		ret = check_status(&task_completed);
		if (ret)
			return ret;

		if (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);
	}
}

This removes the need for the cxd2880_stopwatch_* functions.
> +
> +	ret = cxd2880_tnrdmd_init2(tnr_dmd);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
> new file mode 100644
> index 000000000000..2b4fe5c3743b
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
> @@ -0,0 +1,44 @@
> +/*
> + * cxd2880_integ.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * integration layer common interface
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#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
> -- 
> 2.13.0

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

* Re: [PATCH v4 07/12] [media] cxd2880: Add top level of the driver
  2017-10-13  6:09 ` [PATCH v4 07/12] [media] cxd2880: Add top level of " Yasunari.Takiguchi
@ 2017-12-03 22:59   ` Sean Young
  2017-12-05 11:47     ` Takiguchi, Yasunari
  2017-12-13 19:25   ` Mauro Carvalho Chehab
  1 sibling, 1 reply; 43+ messages in thread
From: Sean Young @ 2017-12-03 22:59 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Hello,

Many thanks for the driver.

The ./scripts/checkpatch.pl with --strict has many warnings, some of which might
be nice to have cleaned up.

There are some very minor comments below. Similar constructs are in other files too.

After reading and understanding through all the code, I think the driver looks
very good, and is ready for merging except for the minor things listed.

Thank you for your submission.

Regards,

Sean


On Fri, Oct 13, 2017 at 03:09:34PM +0900, Yasunari.Takiguchi@sony.com wrote:
> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> This provides the main dvb frontend operation functions
> for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
> 
> [Change list]
> Changes in V4
>    drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
>       -modified typo "inavlid" to "invalid" at pr_err
>       -removed unnecessary initialization at variable declaration
>       -removed unnecessary brace {}
>       -changed to use cxd2880_dvbt_tune and cxd2880_dvbt2_tune 
>        instead of cxd2880_integ_dvbt_tune and cxd2880_integ_dvbt2_tune
>        (because we changed it so that demodulator does not 
>          wait for locking the signal.) 
> 
> Changes in V3
>    drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
>       -adjusted indent spaces
>       -modified debugging code
>       -removed unnecessary cast
>       -modified return code
>       -modified coding style of if() 
>       -modified about measurement period of PER/BER.
>       -changed hexadecimal code to lower case. 
> 
>  drivers/media/dvb-frontends/cxd2880/cxd2880_top.c | 2019 +++++++++++++++++++++
>  1 file changed, 2019 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> new file mode 100644
> index 000000000000..c82aaf0c1597
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> @@ -0,0 +1,2019 @@
> +/*
> + * cxd2880_top.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
> +
> +#include <linux/spi/spi.h>
> +
> +#include "dvb_frontend.h"
> +#include "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;

divermode: this should say drivermode, correct?

> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +	}
> +
> +	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 -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +	}
> +	*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 -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +
> +	*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 -EBUSY;
> +
> +	if (period_exp == 11)
> +		*post_bit_count = 3342336;
> +	else
> +		*post_bit_count = (1U << period_exp) * 204 * 81;
> +
> +	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 -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +	}
> +
> +	*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 -EBUSY;
> +
> +	n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
> +
> +	if (*post_bit_err > ((1U << period_exp) * n_bch))
> +		return -EBUSY;
> +
> +	*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 -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +
> +	*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 -EBUSY;
> +
> +	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 -EPERM;
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +
> +	*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 -EBUSY;
> +
> +	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 = (u16)(((level + 840) * 0xffff) / (-240 + 840));

The cast is not necessary, the implicit cast will do fine.

> +
> +	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 = (u16)snrvalue;

Again cast not necessary.

> +
> +	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)
> +{
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!ber)) {
> +		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_pre_rsber(&priv->tnrdmd,
> +							ber);
> +		/* x100 to change unit.(10^7 -> 10^9) */
> +		*ber *= 100;
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(&priv->tnrdmd,
> +							  ber);
> +	} 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_set_ber_per_period_t(struct dvb_frontend *fe)
> +{
> +	int ret;
> +	struct dtv_frontend_properties *c;
> +	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;
> +	c = &fe->dtv_property_cache;
> +	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 dtv_frontend_properties *c;
> +	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;
> +	c = &fe->dtv_property_cache;
> +	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 -EPERM;
> +
> +	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 -EOPNOTSUPP;
> +	}
> +
> +	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);
> +
> +	ret = cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
> +	if (ret)
> +		return ret;
> +
> +	return 0;

These last four lines can be shortened to:

	return cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);

This construct is repeated many times in cxd2880_tnrdmd_dvbt2.c and 
cxd2880_tnrdmd_dvbt.c

> +}
> +
> +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 -EPERM;
> +
> +	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 -EOPNOTSUPP;
> +	}
> +
> +	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);
> +
> +	ret = cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +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)
> +{
> +	struct cxd2880_priv *priv = NULL;
> +	int ret;
> +
> +	if ((!fe) || (!props)) {
> +		pr_err("invalid arg.");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +
> +	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;
> +
> +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));

Since this is struct, will struct assignment not do?

> +
> +	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);
> +
> +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,
> +};
> +
> +MODULE_DESCRIPTION(
> +"Sony CXD2880 DVB-T2/T tuner + demodulator drvier");

drvier => driver

> +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.13.0

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

* RE: [PATCH v4 07/12] [media] cxd2880: Add top level of the driver
  2017-12-03 22:59   ` Sean Young
@ 2017-12-05 11:47     ` Takiguchi, Yasunari
  0 siblings, 0 replies; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-12-05 11:47 UTC (permalink / raw)
  To: Sean Young
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Takiguchi, Yasunari

Dear Sean

Hi, Thanks for your review.

We will refer to your comments and consider how to respond for them.

> > +	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;
> 
> divermode: this should say drivermode, correct?

diver_mode is not typo, because cxd2880 has diversity function.

> > +MODULE_DESCRIPTION(
> > +"Sony CXD2880 DVB-T2/T tuner + demodulator drvier");
> 
> drvier => driver
Yes. It is typo. We will also re-check other patch files.

Thanks
Takiguchi

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

* Re: [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2017-10-13  5:59 ` [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Yasunari.Takiguchi
  2017-10-13  8:44   ` Honza Petrouš
@ 2017-12-13 17:54   ` Mauro Carvalho Chehab
  2017-12-20  0:54     ` Takiguchi, Yasunari
  1 sibling, 1 reply; 43+ messages in thread
From: Mauro Carvalho Chehab @ 2017-12-13 17:54 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 13 Oct 2017 14:59:28 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> This is the SPI adapter part of the driver for the
> Sony CXD2880 DVB-T2/T tuner + demodulator.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
> 
> [Change list]
> Changes in V4
>    drivers/media/spi/cxd2880-spi.c
>       -removed Camel case
>       -removed unnecessary initialization at variable declaration
>       -removed unnecessary brace {}
> 
> Changes in V3
>    drivers/media/spi/cxd2880-spi.c
>       -adjusted of indent spaces
>       -removed unnecessary cast
>       -changed debugging code
>       -changed timeout method
>       -modified coding style of if()
>       -changed hexadecimal code to lower case. 
> 
> Changes in V2
>    drivers/media/spi/cxd2880-spi.c
>       -Modified PID filter setting.
> 
>  drivers/media/spi/cxd2880-spi.c | 695 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 695 insertions(+)
>  create mode 100644 drivers/media/spi/cxd2880-spi.c
> 
> diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
> new file mode 100644
> index 000000000000..387cb32f90b8
> --- /dev/null
> +++ b/drivers/media/spi/cxd2880-spi.c
> @@ -0,0 +1,695 @@
> +/*
> + * cxd2880-spi.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * SPI adapter
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */

A minor issue (that it is not a show-stopper): we're now using SPDX
instead of repeating the copyright notes everywhere. Please look at
those articles for more details:

	https://blogs.s-osg.org/linux-kernel-license-practices-revisited-spdx/
	https://lwn.net/Articles/739183/

> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__

It would be better to use dev_foo() debug macros instead of
pr_foo() ones.

> +
> +#include <linux/spi/spi.h>
> +#include <linux/jiffies.h>
> +
> +#include "dvb_demux.h"
> +#include "dmxdev.h"
> +#include "dvb_frontend.h"
> +#include "cxd2880.h"
> +
> +#define CXD2880_MAX_FILTER_SIZE 32
> +#define BURST_WRITE_MAX 128
> +#define MAX_TRANS_PACKET 300
> +
> +struct cxd2880_ts_buf_info {
> +	u8 read_ready;
> +	u8 almost_full;
> +	u8 almost_empty;
> +	u8 overflow;
> +	u8 underflow;

Hmm... those seem to be booleans. The best is to declare them as:

	u8 read_ready:1;
	u8 almost_full:1;
	u8 almost_empty:1;
	u8 overflow:1;
	u8 underflow:1;

or to use bool, in order to make it clearer.

> +	u16 packet_num;
> +};
> +
> +struct cxd2880_pid_config {
> +	u8 is_enable;
> +	u16 pid;
> +};
> +
> +struct cxd2880_pid_filter_config {
> +	u8 is_negative;
> +	struct cxd2880_pid_config pid_config[CXD2880_MAX_FILTER_SIZE];
> +};
> +
> +struct cxd2880_dvb_spi {
> +	struct dvb_frontend dvb_fe;
> +	struct dvb_adapter adapter;
> +	struct dvb_demux demux;
> +	struct dmxdev dmxdev;
> +	struct dmx_frontend dmx_fe;
> +	struct task_struct *cxd2880_ts_read_thread;
> +	struct spi_device *spi;
> +	struct mutex spi_mutex; /* For SPI access exclusive control */
> +	int feed_count;
> +	int all_pid_feed_count;
> +	u8 *ts_buf;
> +	struct cxd2880_pid_filter_config filter_config;
> +};
> +
> +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
> +
> +static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size)
> +{
> +	struct spi_message msg;
> +	struct spi_transfer tx;
> +	int ret;
> +
> +	if ((!spi) || (!data)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	memset(&tx, 0, sizeof(tx));
> +	tx.tx_buf = data;
> +	tx.len = size;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&tx, &msg);
> +	ret = spi_sync(spi, &msg);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_write_reg(struct spi_device *spi,
> +			     u8 sub_address, const u8 *data, u32 size)
> +{
> +	u8 send_data[BURST_WRITE_MAX + 4];
> +	const u8 *write_data_top = NULL;
> +	int ret = 0;
> +
> +	if ((!spi) || (!data)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +	if (size > BURST_WRITE_MAX) {
> +		pr_err("data size > WRITE_MAX\n");
> +		return -EINVAL;
> +	}
> +
> +	if (sub_address + size > 0x100) {
> +		pr_err("out of range\n");
> +		return -EINVAL;
> +	}
> +
> +	send_data[0] = 0x0e;
> +	write_data_top = data;
> +
> +	while (size > 0) {
> +		send_data[1] = sub_address;
> +		if (size > 255)
> +			send_data[2] = 255;
> +		else
> +			send_data[2] = (u8)size;
> +
> +		memcpy(&send_data[3], write_data_top, send_data[2]);
> +
> +		ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3);
> +		if (ret) {
> +			pr_err("write spi failed %d\n", ret);
> +			break;
> +		}
> +		sub_address += send_data[2];
> +		write_data_top += send_data[2];
> +		size -= send_data[2];
> +	}
> +
> +	return ret;
> +}
> +
> +static int cxd2880_spi_read_ts(struct spi_device *spi,
> +			       u8 *read_data,
> +			       u32 packet_num)
> +{
> +	int ret;
> +	u8 data[3];
> +	struct spi_message message;
> +	struct spi_transfer transfer[2];
> +
> +	if ((!spi) || (!read_data) || (!packet_num)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +	if (packet_num > 0xffff) {
> +		pr_err("packet num > 0xffff\n");
> +		return -EINVAL;
> +	}
> +
> +	data[0] = 0x10;
> +	data[1] = (packet_num >> 8) & 0xff;
> +	data[2] = packet_num & 0xff;

The & 0xff aren't needed, as the size of each data[] element is 8 bits.

> +
> +	spi_message_init(&message);
> +	memset(transfer, 0, sizeof(transfer));
> +
> +	transfer[0].len = 3;
> +	transfer[0].tx_buf = data;
> +	spi_message_add_tail(&transfer[0], &message);
> +	transfer[1].len = packet_num * 188;
> +	transfer[1].rx_buf = read_data;
> +	spi_message_add_tail(&transfer[1], &message);
> +
> +	ret = spi_sync(spi, &message);
> +	if (ret)
> +		pr_err("spi_write_then_read failed\n");
> +
> +	return ret;
> +}
> +
> +static int cxd2880_spi_read_ts_buffer_info(struct spi_device *spi,
> +					   struct cxd2880_ts_buf_info *info)
> +{
> +	u8 send_data = 0x20;
> +	u8 recv_data[2];
> +	int ret;
> +
> +	if ((!spi) || (!info)) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = spi_write_then_read(spi, &send_data, 1,
> +				  recv_data, sizeof(recv_data));
> +	if (ret)
> +		pr_err("spi_write_then_read failed\n");
> +
> +	info->read_ready = (recv_data[0] & 0x80) ? 1 : 0;
> +	info->almost_full = (recv_data[0] & 0x40) ? 1 : 0;
> +	info->almost_empty = (recv_data[0] & 0x20) ? 1 : 0;
> +	info->overflow = (recv_data[0] & 0x10) ? 1 : 0;
> +	info->underflow = (recv_data[0] & 0x08) ? 1 : 0;

See my comment above. if you use bool, instead of 1/0, you would need
to use true/false here.

> +	info->packet_num = ((recv_data[0] & 0x07) << 8) | recv_data[1];
> +
> +	return ret;
> +}
> +
> +static int cxd2880_spi_clear_ts_buffer(struct spi_device *spi)
> +{
> +	u8 data = 0x03;
> +	int ret;
> +
> +	ret = cxd2880_write_spi(spi, &data, 1);
> +
> +	if (ret)
> +		pr_err("write spi failed\n");
> +
> +	return ret;
> +}
> +
> +static int cxd2880_set_pid_filter(struct spi_device *spi,
> +				  struct cxd2880_pid_filter_config *cfg)
> +{
> +	u8 data[65];
> +	int i;
> +	u16 pid = 0;
> +	int ret;
> +
> +	if (!spi) {
> +		pr_err("ivnalid arg\n");

typo.

> +		return -EINVAL;
> +	}
> +
> +	data[0] = 0x00;
> +	ret = cxd2880_write_reg(spi, 0x00, &data[0], 1);
> +	if (ret)
> +		return ret;
> +	if (!cfg) {
> +		data[0] = 0x02;
> +		ret = cxd2880_write_reg(spi, 0x50, &data[0], 1);
> +		if (ret)
> +			return ret;
> +	} else {
> +		data[0] = cfg->is_negative ? 0x01 : 0x00;
> +
> +		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> +			pid = cfg->pid_config[i].pid;
> +			if (cfg->pid_config[i].is_enable) {
> +				data[1 + (i * 2)] = (pid >> 8) | 0x20;
> +				data[2 + (i * 2)] = pid & 0xff;
> +			} else {
> +				data[1 + (i * 2)] = 0x00;
> +				data[2 + (i * 2)] = 0x00;
> +			}
> +		}
> +		ret = cxd2880_write_reg(spi, 0x50, data, 65);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_update_pid_filter(struct cxd2880_dvb_spi *dvb_spi,
> +				     struct cxd2880_pid_filter_config *cfg,
> +				     bool is_all_pid_filter)
> +{
> +	int ret;
> +
> +	if ((!dvb_spi) || (!cfg)) {
> +		pr_err("invalid arg.\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&dvb_spi->spi_mutex);
> +	if (is_all_pid_filter) {
> +		struct cxd2880_pid_filter_config tmpcfg;
> +
> +		memset(&tmpcfg, 0, sizeof(tmpcfg));
> +		tmpcfg.is_negative = 1;
> +		tmpcfg.pid_config[0].is_enable = 1;
> +		tmpcfg.pid_config[0].pid = 0x1fff;
> +
> +		ret = cxd2880_set_pid_filter(dvb_spi->spi, &tmpcfg);
> +	} else {
> +		ret = cxd2880_set_pid_filter(dvb_spi->spi, cfg);
> +	}
> +	mutex_unlock(&dvb_spi->spi_mutex);
> +
> +	if (ret)
> +		pr_err("set_pid_filter failed\n");
> +
> +	return ret;
> +}
> +
> +static int cxd2880_ts_read(void *arg)
> +{
> +	struct cxd2880_dvb_spi *dvb_spi = NULL;
> +	struct cxd2880_ts_buf_info info;
> +	unsigned int elapsed = 0;
> +	unsigned long start_time = 0;
> +	u32 i;
> +	int ret;
> +
> +	dvb_spi = arg;
> +	if (!dvb_spi) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi);
> +	if (ret) {
> +		pr_err("set_clear_ts_buffer failed\n");
> +		return ret;
> +	}
> +	start_time = jiffies;
> +	while (!kthread_should_stop()) {
> +		elapsed = jiffies_to_msecs(jiffies - start_time);
> +		ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi,
> +						      &info);
> +		if (ret) {
> +			pr_err("spi_read_ts_buffer_info error\n");
> +			return ret;
> +		}
> +
> +		if (info.packet_num > MAX_TRANS_PACKET) {
> +			for (i = 0; i < info.packet_num / MAX_TRANS_PACKET;
> +				i++) {

Please fix intend or place the for on a single line.

> +				cxd2880_spi_read_ts(dvb_spi->spi,
> +						    dvb_spi->ts_buf,
> +						    MAX_TRANS_PACKET);
> +				dvb_dmx_swfilter(&dvb_spi->demux,
> +						 dvb_spi->ts_buf,
> +						 MAX_TRANS_PACKET * 188);
> +			}
> +			start_time = jiffies;
> +		} else if ((info.packet_num > 0) && (elapsed >= 500)) {
> +			cxd2880_spi_read_ts(dvb_spi->spi,
> +					    dvb_spi->ts_buf,
> +					    info.packet_num);
> +			dvb_dmx_swfilter(&dvb_spi->demux,
> +					 dvb_spi->ts_buf,
> +					 info.packet_num * 188);
> +			start_time = jiffies;
> +		} else {
> +			usleep_range(10000, 11000);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_start_feed(struct dvb_demux_feed *feed)
> +{
> +	int ret = 0;
> +	int i = 0;
> +	struct dvb_demux *demux = NULL;
> +	struct cxd2880_dvb_spi *dvb_spi = NULL;
> +
> +	if (!feed) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	demux = feed->demux;
> +	if (!demux) {
> +		pr_err("feed->demux is NULL\n");
> +		return -EINVAL;
> +	}
> +	dvb_spi = demux->priv;
> +
> +	if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) {
> +		pr_err("Exceeded maximum PID count (32).");
> +		pr_err("Selected PID cannot be enabled.\n");
> +		return -EBUSY;
> +	}
> +
> +	if (feed->pid == 0x2000) {
> +		if (dvb_spi->all_pid_feed_count == 0) {
> +			ret = cxd2880_update_pid_filter(dvb_spi,
> +							&dvb_spi->filter_config,
> +							true);
> +			if (ret) {
> +				pr_err("update pid filter failed\n");
> +				return ret;
> +			}
> +		}
> +		dvb_spi->all_pid_feed_count++;
> +
> +		pr_debug("all PID feed (count = %d)\n",
> +			 dvb_spi->all_pid_feed_count);
> +	} else {
> +		struct cxd2880_pid_filter_config cfgtmp;
> +
> +		cfgtmp = dvb_spi->filter_config;
> +
> +		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> +			if (cfgtmp.pid_config[i].is_enable == 0) {
> +				cfgtmp.pid_config[i].is_enable = 1;
> +				cfgtmp.pid_config[i].pid = feed->pid;
> +				pr_debug("store PID %d to #%d\n",
> +					 feed->pid, i);
> +				break;
> +			}
> +		}
> +		if (i == CXD2880_MAX_FILTER_SIZE) {
> +			pr_err("PID filter is full. Assumed bug.\n");
> +			return -EBUSY;
> +		}
> +		if (!dvb_spi->all_pid_feed_count)
> +			ret = cxd2880_update_pid_filter(dvb_spi,
> +							&cfgtmp,
> +							false);
> +		if (ret)
> +			return ret;
> +
> +		dvb_spi->filter_config = cfgtmp;
> +	}
> +
> +	if (dvb_spi->feed_count == 0) {
> +		dvb_spi->ts_buf =
> +			kmalloc(MAX_TRANS_PACKET * 188,
> +				GFP_KERNEL | GFP_DMA);
> +		if (!dvb_spi->ts_buf) {
> +			pr_err("ts buffer allocate failed\n");
> +			memset(&dvb_spi->filter_config, 0,
> +			       sizeof(dvb_spi->filter_config));
> +			dvb_spi->all_pid_feed_count = 0;
> +			return -ENOMEM;
> +		}
> +		dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read,
> +							      dvb_spi,
> +							      "cxd2880_ts_read");
> +		if (IS_ERR(dvb_spi->cxd2880_ts_read_thread)) {
> +			pr_err("kthread_run failed/\n");
> +			kfree(dvb_spi->ts_buf);
> +			dvb_spi->ts_buf = NULL;
> +			memset(&dvb_spi->filter_config, 0,
> +			       sizeof(dvb_spi->filter_config));
> +			dvb_spi->all_pid_feed_count = 0;
> +			return PTR_ERR(dvb_spi->cxd2880_ts_read_thread);
> +		}
> +	}
> +
> +	dvb_spi->feed_count++;
> +
> +	pr_debug("start feed (count %d)\n", dvb_spi->feed_count);
> +	return 0;
> +}
> +
> +static int cxd2880_stop_feed(struct dvb_demux_feed *feed)
> +{
> +	int i = 0;
> +	int ret;
> +	struct dvb_demux *demux = NULL;
> +	struct cxd2880_dvb_spi *dvb_spi = NULL;
> +
> +	if (!feed) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	demux = feed->demux;
> +	if (!demux) {
> +		pr_err("feed->demux is NULL\n");
> +		return -EINVAL;
> +	}
> +	dvb_spi = demux->priv;
> +
> +	if (!dvb_spi->feed_count) {
> +		pr_err("no feed is started\n");
> +		return -EINVAL;
> +	}
> +
> +	if (feed->pid == 0x2000) {
> +		/*
> +		 * Special PID case.
> +		 * Number of 0x2000 feed request was stored
> +		 * in dvb_spi->all_pid_feed_count.
> +		 */
> +		if (dvb_spi->all_pid_feed_count <= 0) {
> +			pr_err("PID %d not found.\n", feed->pid);
> +			return -EINVAL;
> +		}
> +		dvb_spi->all_pid_feed_count--;
> +	} else {
> +		struct cxd2880_pid_filter_config cfgtmp;
> +
> +		cfgtmp = dvb_spi->filter_config;
> +
> +		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> +			if (feed->pid == cfgtmp.pid_config[i].pid &&
> +			    cfgtmp.pid_config[i].is_enable != 0) {
> +				cfgtmp.pid_config[i].is_enable = 0;
> +				cfgtmp.pid_config[i].pid = 0;
> +				pr_debug("removed PID %d from #%d\n",
> +					 feed->pid, i);
> +				break;
> +			}
> +		}
> +		dvb_spi->filter_config = cfgtmp;
> +
> +		if (i == CXD2880_MAX_FILTER_SIZE) {
> +			pr_err("PID %d not found\n", feed->pid);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	ret = cxd2880_update_pid_filter(dvb_spi,
> +					&dvb_spi->filter_config,
> +					dvb_spi->all_pid_feed_count > 0);
> +	dvb_spi->feed_count--;
> +
> +	if (dvb_spi->feed_count == 0) {
> +		int ret_stop = 0;
> +
> +		ret_stop = kthread_stop(dvb_spi->cxd2880_ts_read_thread);
> +		if (ret_stop) {
> +			pr_err("'kthread_stop failed. (%d)\n", ret_stop);
> +			ret = ret_stop;
> +		}
> +		kfree(dvb_spi->ts_buf);
> +		dvb_spi->ts_buf = NULL;
> +	}
> +
> +	pr_debug("stop feed ok.(count %d)\n", dvb_spi->feed_count);
> +
> +	return ret;
> +}

I have the feeling that I've seen the code above before at the dvb core.
Any reason why don't use the already-existing code at dvb_demux.c & friends?

> +
> +static const struct of_device_id cxd2880_spi_of_match[] = {
> +	{ .compatible = "sony,cxd2880" },
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match);
> +
> +static int
> +cxd2880_spi_probe(struct spi_device *spi)
> +{
> +	int ret;
> +	struct cxd2880_dvb_spi *dvb_spi = NULL;
> +	struct cxd2880_config config;
> +
> +	if (!spi) {
> +		pr_err("invalid arg.\n");
> +		return -EINVAL;
> +	}
> +
> +	dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL);
> +	if (!dvb_spi)
> +		return -ENOMEM;
> +
> +	dvb_spi->spi = spi;
> +	mutex_init(&dvb_spi->spi_mutex);
> +	dev_set_drvdata(&spi->dev, dvb_spi);
> +	config.spi = spi;
> +	config.spi_mutex = &dvb_spi->spi_mutex;
> +
> +	ret = dvb_register_adapter(&dvb_spi->adapter,
> +				   "CXD2880",
> +				   THIS_MODULE,
> +				   &spi->dev,
> +				   adapter_nr);
> +	if (ret < 0) {
> +		pr_err("dvb_register_adapter() failed\n");
> +		goto fail_adapter;
> +	}
> +
> +	if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
> +		pr_err("cxd2880_attach failed\n");
> +		goto fail_attach;
> +	}
> +
> +	ret = dvb_register_frontend(&dvb_spi->adapter,
> +				    &dvb_spi->dvb_fe);
> +	if (ret < 0) {
> +		pr_err("dvb_register_frontend() failed\n");
> +		goto fail_frontend;
> +	}
> +
> +	dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING;
> +	dvb_spi->demux.priv = dvb_spi;
> +	dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE;
> +	dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE;
> +	dvb_spi->demux.start_feed = cxd2880_start_feed;
> +	dvb_spi->demux.stop_feed = cxd2880_stop_feed;
> +
> +	ret = dvb_dmx_init(&dvb_spi->demux);
> +	if (ret < 0) {
> +		pr_err("dvb_dmx_init() failed\n");
> +		goto fail_dmx;
> +	}
> +
> +	dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE;
> +	dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx;
> +	dvb_spi->dmxdev.capabilities = 0;
> +	ret = dvb_dmxdev_init(&dvb_spi->dmxdev,
> +			      &dvb_spi->adapter);
> +	if (ret < 0) {
> +		pr_err("dvb_dmxdev_init() failed\n");
> +		goto fail_dmxdev;
> +	}
> +
> +	dvb_spi->dmx_fe.source = DMX_FRONTEND_0;
> +	ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx,
> +					      &dvb_spi->dmx_fe);
> +	if (ret < 0) {
> +		pr_err("add_frontend() failed\n");
> +		goto fail_dmx_fe;
> +	}
> +
> +	ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx,
> +						  &dvb_spi->dmx_fe);
> +	if (ret < 0) {
> +		pr_err("dvb_register_frontend() failed\n");
> +		goto fail_fe_conn;
> +	}
> +
> +	pr_info("Sony CXD2880 has successfully attached.\n");
> +
> +	return 0;
> +
> +fail_fe_conn:
> +	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
> +					   &dvb_spi->dmx_fe);
> +fail_dmx_fe:
> +	dvb_dmxdev_release(&dvb_spi->dmxdev);
> +fail_dmxdev:
> +	dvb_dmx_release(&dvb_spi->demux);
> +fail_dmx:
> +	dvb_unregister_frontend(&dvb_spi->dvb_fe);
> +fail_frontend:
> +	dvb_frontend_detach(&dvb_spi->dvb_fe);
> +fail_attach:
> +	dvb_unregister_adapter(&dvb_spi->adapter);
> +fail_adapter:
> +	kfree(dvb_spi);
> +	return ret;
> +}
> +
> +static int
> +cxd2880_spi_remove(struct spi_device *spi)
> +{
> +	struct cxd2880_dvb_spi *dvb_spi;
> +
> +	if (!spi) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	dvb_spi = dev_get_drvdata(&spi->dev);
> +
> +	if (!dvb_spi) {
> +		pr_err("failed\n");
> +		return -EINVAL;
> +	}
> +	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
> +					   &dvb_spi->dmx_fe);
> +	dvb_dmxdev_release(&dvb_spi->dmxdev);
> +	dvb_dmx_release(&dvb_spi->demux);
> +	dvb_unregister_frontend(&dvb_spi->dvb_fe);
> +	dvb_frontend_detach(&dvb_spi->dvb_fe);
> +	dvb_unregister_adapter(&dvb_spi->adapter);
> +
> +	kfree(dvb_spi);
> +	pr_info("cxd2880_spi remove ok.\n");
> +
> +	return 0;
> +}
> +
> +static const struct spi_device_id cxd2880_spi_id[] = {
> +	{ "cxd2880", 0 },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(spi, cxd2880_spi_id);
> +
> +static struct spi_driver cxd2880_spi_driver = {
> +	.driver	= {
> +		.name	= "cxd2880",
> +		.of_match_table = cxd2880_spi_of_match,
> +	},
> +	.id_table = cxd2880_spi_id,
> +	.probe    = cxd2880_spi_probe,
> +	.remove   = cxd2880_spi_remove,
> +};
> +module_spi_driver(cxd2880_spi_driver);
> +
> +MODULE_DESCRIPTION(
> +"Sony CXD2880 DVB-T2/T tuner + demodulator drvier SPI adapter");

Just put it on a single line.

There's a typo there too: drvier

> +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
> +MODULE_LICENSE("GPL v2");



Thanks,
Mauro

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

* Re: [PATCH v4 03/12] [media] cxd2880: Add common files for the driver
  2017-10-13  6:02 ` [PATCH v4 03/12] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
@ 2017-12-13 18:02   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 43+ messages in thread
From: Mauro Carvalho Chehab @ 2017-12-13 18:02 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 13 Oct 2017 15:02:59 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> These are common files for the driver for the
> Sony CXD2880 DVB-T2/T tuner + demodulator.
> These contains helper functions for the driver.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
> 
> [Change list]
> Changes in V4
>    drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
>       -removed unnecessary initialization at variable declaration
>       -modified how to write consecutive registers
> 
> Changes in V3
>    drivers/media/dvb-frontends/cxd2880/cxd2880.h
>       -no change
>    drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
>       -changed MASKUPPER/MASKLOWER with GENMASK 
>    drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
>       -removed definition NULL and SONY_SLEEP
>       -changed CXD2880_SLEEP to usleep_range
>       -changed cxd2880_atomic_set to atomic_set
>       -removed cxd2880_atomic struct and cxd2880_atomic_read
>       -changed stop-watch function
>       -modified return code
>    drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
>       -removed unnecessary cast
>       -modified return code
>       -changed hexadecimal code to lower case. 
>    drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
>       -modified return code 
>    drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
>       -changed CXD2880_SLEEP to usleep_range
>       -changed stop-watch function
>       -modified return code
>    #drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
>       -cxd2880_stdlib.h file was removed from V3.
> 
>  drivers/media/dvb-frontends/cxd2880/cxd2880.h      | 46 +++++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_common.c   | 38 +++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_common.h   | 50 ++++++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_io.c   | 89 ++++++++++++++++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_io.h   | 71 +++++++++++++++++
>  .../dvb-frontends/cxd2880/cxd2880_stopwatch_port.c | 60 +++++++++++++++
>  6 files changed, 354 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
> new file mode 100644
> index 000000000000..281f9a784eb5
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
> @@ -0,0 +1,46 @@
> +/*
> + * cxd2880.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */

Same comment made on patch 2 applies to this one and to the entire
series, with regards to SPDX.

> +
> +#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 */
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
> new file mode 100644
> index 000000000000..ffaa140bb8cb
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
> @@ -0,0 +1,38 @@
> +/*
> + * cxd2880_common.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_common.h"
> +
> +int cxd2880_convert2s_complement(u32 value, u32 bitlen)
> +{
> +	if ((bitlen == 0) || (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);
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
> new file mode 100644
> index 000000000000..cf6b800809ee
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
> @@ -0,0 +1,50 @@
> +/*
> + * cxd2880_common.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_COMMON_H
> +#define CXD2880_COMMON_H
> +
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/string.h>
> +
> +#define CXD2880_ARG_UNUSED(arg) ((void)(arg))

Huh??? Why this is needed?

> +
> +int cxd2880_convert2s_complement(u32 value, u32 bitlen);
> +
> +struct cxd2880_stopwatch {
> +	unsigned long start_time;
> +};
> +
> +int cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch);
> +
> +int cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
> +			    u32 ms);
> +
> +int cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
> +			      unsigned int *elapsed);
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
> new file mode 100644
> index 000000000000..bdb0b7982401
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
> @@ -0,0 +1,89 @@
> +/*
> + * cxd2880_io.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * register I/O interface functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#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)
> +{
> +	int ret;
> +
> +	if (!io)
> +		return -EINVAL;
> +
> +	ret = io->write_regs(io, tgt, sub_address, &data, 1);
> +
> +	return ret;
> +}
> +
> +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));
> +	}
> +
> +	ret = io->write_reg(io, tgt, sub_address, data);
> +
> +	return ret;
> +}
> +
> +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;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
> new file mode 100644
> index 000000000000..f7aee6c6480e
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
> @@ -0,0 +1,71 @@
> +/*
> + * cxd2880_io.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * register I/O interface definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#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
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
> new file mode 100644
> index 000000000000..a4a1e29de653
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
> @@ -0,0 +1,60 @@
> +/*
> + * cxd2880_stopwatch_port.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * time measurement functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/jiffies.h>
> +
> +#include "cxd2880_common.h"
> +
> +int cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch)
> +{
> +	if (!stopwatch)
> +		return -EINVAL;
> +
> +	stopwatch->start_time = jiffies;
> +
> +	return 0;
> +}
> +
> +int cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
> +			    u32 ms)
> +{
> +	if (!stopwatch)
> +		return -EINVAL;
> +	CXD2880_ARG_UNUSED(*stopwatch);

Huh? This macro does nothing. If this argument is not needed, then
just don't declare it...

> +	usleep_range(ms * 10000, ms * 10000 + 1000);
> +
> +	return 0;
> +}

... at the end, this code evaluates to just:
	usleep_range(ms * 10000, ms * 10000 + 1000);

Why don't you just use usleep_range() directly, instead of adding
a function that just encapsulates it?

> +
> +int cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
> +			      unsigned int *elapsed)
> +{
> +	if (!stopwatch || !elapsed)
> +		return -EINVAL;
> +	*elapsed = jiffies_to_msecs(jiffies - stopwatch->start_time);
> +
> +	return 0;
> +}


It seems that this entire C file is just adding another layer on the
top of jiffies. Please, don't do that, as it makes a lot harder to
understand what you're trying to do.


Thanks,
Mauro

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

* Re: [PATCH v4 05/12] [media] cxd2880: Add tuner part of the driver
  2017-10-13  6:07 ` [PATCH v4 05/12] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
@ 2017-12-13 18:40   ` Mauro Carvalho Chehab
  2017-12-20  1:01     ` Takiguchi, Yasunari
  0 siblings, 1 reply; 43+ messages in thread
From: Mauro Carvalho Chehab @ 2017-12-13 18:40 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 13 Oct 2017 15:07:25 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> This part of the driver has the main routines to handle
> the tuner and demodulator functionality.  The tnrdmd_mon.* files
> have monitor functions for the driver.
> This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
> 
> [Change list]
> Changes in V4
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
>       -used over 80 columns limit, it makes fine to read codes
>       -removed unnecessary initialization at variable declaration
>       -modified how to write consecutive registers
>       -removed unnecessary brace {}
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
>       -adjusted of indent spaces of macro
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
>       -updated version information
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
>       -removed unnecessary brace {}
> 
> Changes in V3
>    drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
>       -removed code relevant to ISDB-T
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
>       -removed unnecessary cast
>       -removed code relevant to ISDB-T
>       -changed CXD2880_SLEEP to usleep_range
>       -changed cxd2880_memset to memset 
>       -changed cxd2880_atomic_set to atomic_set
>       -modified return code
>       -modified coding style of if()
>       -changed to use const values at writing a lot of registers 
>        with a command. 
>       -changed hexadecimal code to lower case. 
>       -adjusted of indent spaces
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
>       -removed code relevant to ISDB-T
>       -changed cxd2880_atomic struct to atomic_t
>       -modified return code
>       -changed hexadecimal code to lower case. 
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
>       -updated version information
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
>       -changed CXD2880_SLEEP to usleep_range
>       -removed unnecessary cast
>       -modified return code
>       -modified coding style of if() 
>       -changed hexadecimal code to lower case. 
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
>       -modified return code
> 
> Changes in V2
>    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
>       -updated version information
> 
>  drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h  |   46 +
>  .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c   | 3687 ++++++++++++++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h   |  391 +++
>  .../cxd2880/cxd2880_tnrdmd_driver_version.h        |   29 +
>  .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c     |  218 ++
>  .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h     |   52 +
>  6 files changed, 4423 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
> new file mode 100644
> index 000000000000..2d35d3990060
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
> @@ -0,0 +1,46 @@
> +/*
> + * cxd2880_dtv.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * DTV related definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#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
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
> new file mode 100644
> index 000000000000..17fd0bc6249c
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
> @@ -0,0 +1,3687 @@
> +/*
> + * cxd2880_tnrdmd.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common control functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "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 -EOPNOTSUPP;

Hmm... this error code is used on DVB to indicate that an ioctl
is not supported. If a parameter is not valid, the right error
code is -EINVAL.

Please see:
	https://linuxtv.org/downloads/v4l-dvb-apis-new/uapi/gen-errors.html

for a (non-complete) list of error codes. The same note applies to other
similar checks.

> +	}
> +
> +	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 -EOPNOTSUPP;
> +	}
> +
> +	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> +				     CXD2880_IO_TGT_SYS,
> +				     0x69, data);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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;
> +	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> +				      CXD2880_IO_TGT_SYS,
> +				      0x13,
> +				      data,
> +				      6);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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;
> +	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> +				      CXD2880_IO_TGT_SYS,
> +				      0x1f, data, 2);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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 -EBUSY;

I don't know anything about this hardware, but it sounds weird to return
-EBUSY here, except if the hardware reached a permanent busy condition,
and would require some sort of reset to work again.

As this is in the middle of lots of things, I *suspect* that this is
not the case.

If I'm right, and this is just a transitory solution that could happen
for a limited amount of time, e. g. if what's there at data[0] is a flag
saying that the device didn't finish the last operation yet, maybe the
best would be to do something like:

	for (i = 0; i < 10; i++) {
		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
					     CXD2880_IO_TGT_SYS,
					     0x10, data, 1);
		if (ret)
			return ret;
		if (data[0] & 0x01)
			break;
		msleep(10);
	}
	if (!(data[0] & 0x01))
		return -EBUSY;

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

Same here and on similar places.

> +
> +	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 -EBUSY;
> +
> +	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> +					  CXD2880_IO_TGT_SYS,
> +					  rf_init1_seq9,
> +					  ARRAY_SIZE(rf_init1_seq9));
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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;
> +
> +	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> +					  CXD2880_IO_TGT_DMD,
> +					  rf_init2_seq2,
> +					  ARRAY_SIZE(rf_init2_seq2));
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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[0] = 0x00;
> +	data[1] = 0x00;

The data struct was already zeroed when it was created with { 0 }.
So, you only need to fill the values that aren't zero.

> +	data[2] = 0x0e;
> +	data[3] = 0x00;
> +	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;
> +	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> +				      CXD2880_IO_TGT_SYS,
> +				      0x52, data, 11);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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;
> +	}
> +
> +	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> +					  CXD2880_IO_TGT_DMD,
> +					  x_tune2_seq5,
> +					  ARRAY_SIZE(x_tune2_seq5));
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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;
> +
> +	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> +				     CXD2880_IO_TGT_DMD,
> +				     0xb1, data[0]);
> +	if (ret)
> +		return ret;
> +
> +	return 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;
> +
> +	ret = cxd2880_io_write_multi_regs(tnr_dmd->diver_sub->io,
> +					  CXD2880_IO_TGT_DMD,
> +					  x_tune4_seq,
> +					  ARRAY_SIZE(x_tune4_seq));
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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;
> +	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> +				      CXD2880_IO_TGT_SYS,
> +				      0x55, data, 2);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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 -EBUSY;
> +
> +	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
> +					  CXD2880_IO_TGT_SYS,
> +					  x_sleep2_seq2,
> +					  ARRAY_SIZE(x_sleep2_seq2));
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int x_sleep3(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,
> +					  x_sleep3_seq,
> +					  ARRAY_SIZE(x_sleep3_seq));
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int x_sleep4(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,
> +					  x_sleep4_seq,
> +					  ARRAY_SIZE(x_sleep4_seq));
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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 -EBUSY;
> +
> +	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));
> +	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> +				      CXD2880_IO_TGT_SYS,
> +				      0x26, data, 4);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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 -EBUSY;
> +
> +		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 -EBUSY;
> +	}
> +
> +	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 -EBUSY;
> +
> +	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));
> +	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> +				      CXD2880_IO_TGT_SYS,
> +				      0x27, data, 3);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	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;
> +
> +	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) {
> +		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> +					     CXD2880_IO_TGT_DMD,
> +					     0x50, 0x02);
> +		if (ret)
> +			return ret;

Just do:
		return ret;

as there's nothing else to be done in this case.

> +	} else {

With the above change, you can remove this else, with removes one
indentation from the code, making easier to read.

> +		u8 data[65];
> +
> +		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;
> +			}
> +		}
> +		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> +					      CXD2880_IO_TGT_DMD,
> +					      0x50, data, 65);
> +		if (ret)
> +			return ret;

Again, just
		return ret;

is enough.

> +	}
> +
> +	return 0;
> +}
> +
> +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 ret;

better to use:
	return 0;

as otherwise, gcc may complain about the usage of an uninitialized var, when
compiling with W=1.

> +}
> +
> +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 == 0) {

Better to invert the condition here and remove one indent:

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

Same here: if you invert the condition, checking first for overflow,
you can reduce another indent, preventing 80 cols warnings and making
simpler to review.

> +		}
> +	}
> +
> +	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 -EOPNOTSUPP;
> +
> +	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 -EOPNOTSUPP;
> +	}
> +
> +	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);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	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 -EBUSY;
> +
> +	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 -EPERM;
> +
> +	if (frequency_khz < 4000)
> +		return -ERANGE;
> +
> +	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 -EBUSY;
> +
> +	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);
> +		if (ret)
> +			return ret;
> +	} 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);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	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 -EPERM;
> +
> +	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;
> +	}
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret = x_tune4(tnr_dmd);
> +		if (ret)
> +			return ret;
> +	}

Hmm... both ifs are checking the same thing. Just merge them.

> +
> +	ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1);
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +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 -EPERM;

-EPERM? What a state has to do with Linux permissions? This should
very likely be -EINVAL or -EBUSY. -EPERM is usually reserved to check
if the user has permissions to open/read/write (or to execute
some other syscall).

Very likely, the same applies to all places where this driver is
returning -EPERM.

> +
> +	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 -EPERM;
> +	}
> +
> +	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;
> +	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 -EPERM;
> +
> +	switch (id) {
> +	case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return -EPERM;
> +
> +		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 -EPERM;
> +
> +		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 -EPERM;
> +
> +		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 -EPERM;
> +
> +		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 -EPERM;
> +
> +		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 -EPERM;
> +
> +		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 -EPERM;
> +
> +		if ((value < 0) || (value > 0x1f))
> +			return -ERANGE;
> +
> +		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 -EPERM;
> +
> +		if ((value < 0) || (value > 0x1f))
> +			return -ERANGE;
> +
> +		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 -EPERM;
> +
> +		if ((value < 0) || (value > 0x1f))
> +			return -ERANGE;
> +
> +		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 -EPERM;
> +
> +		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 -EPERM;
> +
> +		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 -EPERM;
> +
> +		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 -EPERM;
> +
> +		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 -EPERM;
> +
> +		if ((value < 0) || (value > 1))
> +			return -ERANGE;
> +
> +		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 -EPERM;
> +
> +		if ((value < 0) || (value > 0xff))
> +			return -ERANGE;
> +
> +		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 -EPERM;
> +
> +		if ((value < 0) || (value > 7))
> +			return -ERANGE;
> +
> +		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 -EPERM;
> +
> +		tnr_dmd->is_ts_backwards_compatible_mode = value ? 1 : 0;
> +
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_PWM_VALUE:
> +		if ((value < 0) || (value > 0x1000))
> +			return -ERANGE;
> +
> +		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 -ERANGE;
> +		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 -ERANGE;
> +
> +		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 -ERANGE;
> +
> +		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 -ERANGE;
> +
> +		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 -ERANGE;
> +
> +		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 -ERANGE;
> +
> +		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 -ERANGE;
> +
> +		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);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +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 -EPERM;
> +
> +	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;
> +
> +	ret =
> +	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> +						 0x00, 0x45,
> +						 en ? 0 : (1 << id),
> +						 1 << id);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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 ret;
> +
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return -EINVAL;
> +
> +	ret =
> +	    cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode,
> +					open_drain, invert);
> +
> +	return ret;
> +}
> +
> +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 -EPERM;
> +
> +	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)
> +{
> +	int ret;
> +
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return -EINVAL;
> +
> +	ret = cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value);
> +
> +	return ret;
> +}
> +
> +int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
> +			      u8 id, u8 value)
> +{
> +	int ret;
> +
> +	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 -EPERM;
> +
> +	ret = cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +						   CXD2880_IO_TGT_SYS,
> +						   0x00, 0x46,
> +						   value ? (1 << id) : 0,
> +						   1 << id);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +				  u8 id, u8 value)
> +{
> +	int ret;
> +
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return -EINVAL;
> +
> +	ret = cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value);
> +
> +	return ret;
> +}
> +
> +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 -EPERM;
> +
> +	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 -EPERM;
> +
> +	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;
> +	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> +				      CXD2880_IO_TGT_SYS,
> +				      0x3c, data, 2);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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 -EPERM;
> +
> +	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;
> +	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
> +				      CXD2880_IO_TGT_DMD,
> +				      0x9f, data, 2);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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 -EPERM;
> +
> +	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;
> +
> +	ret = set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask);
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +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 -EPERM;
> +
> +	CXD2880_ARG_UNUSED(sys);
> +
> +	tnr_dmd->scan_mode = scan_mode_end;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		int ret;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys,
> +						 scan_mode_end);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
> +			       struct cxd2880_tnrdmd_pid_ftr_cfg
> +			       *pid_ftr_cfg)
> +{
> +	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 -EPERM;
> +
> +	if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS)
> +		return -EOPNOTSUPP;
> +
> +	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) {
> +		ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	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 *))
> +{
> +	int ret;
> +
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return -EINVAL;
> +
> +	ret = cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub,
> +					       rf_lvl_cmpstn);
> +
> +	return ret;
> +}
> +
> +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)
> +{
> +	int ret;
> +
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return -EINVAL;
> +
> +	ret = cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub,
> +					  tbl_air, tbl_cable);
> +
> +	return ret;
> +}
> +
> +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 -EPERM;
> +
> +	if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS)
> +		return -EOPNOTSUPP;

Again, EOPNOTSUPP is wrong. You should review all error codes, as there
are some very weird things with regards to it all over this patch.

> +
> +	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));
> +		if (ret)
> +			return ret;
> +	} 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);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +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 -EPERM;
> +
> +	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 -EPERM;
> +	}
> +
> +	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;
> +	}
> +
> +	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> +				     CXD2880_IO_TGT_DMD,
> +				     0x01, 0x01);
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
> new file mode 100644
> index 000000000000..d740da0ff868
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
> @@ -0,0 +1,391 @@
> +/*
> + * cxd2880_tnrdmd.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common control interface
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#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_ts_buf_info {
> +	u8 read_ready;
> +	u8 almost_full;
> +	u8 almost_empty;
> +	u8 overflow;
> +	u8 underflow;
> +	u16 packet_num;
> +};
> +
> +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
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> new file mode 100644
> index 000000000000..ff29e747d5b7
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> @@ -0,0 +1,29 @@
> +/*
> + * cxd2880_tnrdmd_driver_version.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * version information
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.3"
> +
> +#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2017-10-03"
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
> new file mode 100644
> index 000000000000..2b40b25ee2b8
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
> @@ -0,0 +1,218 @@
> +/*
> + * cxd2880_tnrdmd_mon.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common monitor functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#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 -EPERM;
> +
> +	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] != 0) || (rdata[1] != 0))
> +		return -EPERM;
> +
> +	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);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +				  int *rf_lvl_db)
> +{
> +	int ret;
> +
> +	if ((!tnr_dmd) || (!rf_lvl_db))
> +		return -EINVAL;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return -EINVAL;
> +
> +	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db);
> +
> +	return ret;
> +}
> +
> +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)
> +{
> +	int ret;
> +
> +	if ((!tnr_dmd) || (!status))
> +		return -EINVAL;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return -EINVAL;
> +
> +	ret =
> +	    cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub, status);
> +
> +	return ret;
> +}
> +
> +int cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
> +				   struct
> +				   cxd2880_tnrdmd_ts_buf_info
> +				   *info)
> +{
> +	u8 data[3] = { 0 };
> +	int ret;
> +
> +	if ((!tnr_dmd) || (!info))
> +		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 -EPERM;
> +
> +	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> +				     CXD2880_IO_TGT_DMD,
> +				     0x00, 0x0a);
> +	if (ret)
> +		return ret;
> +	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> +				     CXD2880_IO_TGT_DMD,
> +				     0x50, data, 3);
> +	if (ret)
> +		return ret;
> +
> +	info->read_ready = (data[0] & 0x10) ? 0x01 : 0x00;
> +	info->almost_full = (data[0] & 0x08) ? 0x01 : 0x00;
> +	info->almost_empty = (data[0] & 0x04) ? 0x01 : 0x00;
> +	info->overflow = (data[0] & 0x02) ? 0x01 : 0x00;
> +	info->underflow = (data[0] & 0x01) ? 0x01 : 0x00;
> +
> +	info->packet_num = ((data[1] & 0x07) << 8) | data[2];
> +
> +	return ret;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
> new file mode 100644
> index 000000000000..a4c34f56a7a1
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
> @@ -0,0 +1,52 @@
> +/*
> + * cxd2880_tnrdmd_mon.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common monitor interface
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#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);
> +
> +int cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
> +				   struct
> +				   cxd2880_tnrdmd_ts_buf_info
> +				   *info);
> +
> +#endif



Thanks,
Mauro

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

* Re: [PATCH v4 06/12] [media] cxd2880: Add integration layer for the driver
  2017-10-13  6:08 ` [PATCH v4 06/12] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
  2017-12-03 18:23   ` Sean Young
@ 2017-12-13 19:13   ` Mauro Carvalho Chehab
  2017-12-20  1:06     ` Takiguchi, Yasunari
  1 sibling, 1 reply; 43+ messages in thread
From: Mauro Carvalho Chehab @ 2017-12-13 19:13 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 13 Oct 2017 15:08:34 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> These functions monitor the driver and watch for task completion.
> This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

If I understand well, the goal here is to have thread that would
be waking up from time to time, right? Just use the infrastructure
that the Kernel has for it, like a kthread, or timer_setup() & friends.

Take a look at include/linux/timer.h, and just use what's already
defined.


> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
> 
> [Change list]
> Changes in V4   
>    drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
>       -removed unnecessary initialization at variable declaration
> 
> Changes in V3
>    drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
>       -changed cxd2880_atomic_read to atomic_read
>       -changed cxd2880_atomic_set to atomic_set
>       -modified return code
>       -modified coding style of if() 
>    drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
>       -modified return code
> 
>  .../media/dvb-frontends/cxd2880/cxd2880_integ.c    | 98 ++++++++++++++++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_integ.h    | 44 ++++++++++
>  2 files changed, 142 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
> new file mode 100644
> index 000000000000..7264fc355d6b
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
> @@ -0,0 +1,98 @@
> +/*
> + * cxd2880_integ.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * integration layer common functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_tnrdmd.h"
> +#include "cxd2880_tnrdmd_mon.h"
> +#include "cxd2880_integ.h"
> +
> +int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	int ret;
> +	struct cxd2880_stopwatch timer;
> +	unsigned int elapsed_time = 0;
> +	u8 cpu_task_completed = 0;
> +
> +	if (!tnr_dmd)
> +		return -EINVAL;
> +
> +	ret = cxd2880_tnrdmd_init1(tnr_dmd);
> +	if (ret)
> +		return ret;
> +
> +	ret = cxd2880_stopwatch_start(&timer);
> +	if (ret)
> +		return ret;
> +
> +	while (1) {
> +		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed_time);
> +		if (ret)
> +			return ret;
> +
> +		ret =
> +		    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
> +						     &cpu_task_completed);
> +		if (ret)
> +			return ret;
> +
> +		if (cpu_task_completed)
> +			break;
> +
> +		if (elapsed_time > CXD2880_TNRDMD_WAIT_INIT_TIMEOUT)
> +			return -ETIME;
> +		ret =
> +		    cxd2880_stopwatch_sleep(&timer,
> +					    CXD2880_TNRDMD_WAIT_INIT_INTVL);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = cxd2880_tnrdmd_init2(tnr_dmd);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
> new file mode 100644
> index 000000000000..2b4fe5c3743b
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
> @@ -0,0 +1,44 @@
> +/*
> + * cxd2880_integ.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * integration layer common interface
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#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



Thanks,
Mauro

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

* Re: [PATCH v4 07/12] [media] cxd2880: Add top level of the driver
  2017-10-13  6:09 ` [PATCH v4 07/12] [media] cxd2880: Add top level of " Yasunari.Takiguchi
  2017-12-03 22:59   ` Sean Young
@ 2017-12-13 19:25   ` Mauro Carvalho Chehab
  2017-12-20  1:18     ` Takiguchi, Yasunari
  1 sibling, 1 reply; 43+ messages in thread
From: Mauro Carvalho Chehab @ 2017-12-13 19:25 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 13 Oct 2017 15:09:34 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> This provides the main dvb frontend operation functions
> for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
> 
> [Change list]
> Changes in V4
>    drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
>       -modified typo "inavlid" to "invalid" at pr_err
>       -removed unnecessary initialization at variable declaration
>       -removed unnecessary brace {}
>       -changed to use cxd2880_dvbt_tune and cxd2880_dvbt2_tune 
>        instead of cxd2880_integ_dvbt_tune and cxd2880_integ_dvbt2_tune
>        (because we changed it so that demodulator does not 
>          wait for locking the signal.) 
> 
> Changes in V3
>    drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
>       -adjusted indent spaces
>       -modified debugging code
>       -removed unnecessary cast
>       -modified return code
>       -modified coding style of if() 
>       -modified about measurement period of PER/BER.
>       -changed hexadecimal code to lower case. 
> 
>  drivers/media/dvb-frontends/cxd2880/cxd2880_top.c | 2019 +++++++++++++++++++++
>  1 file changed, 2019 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> new file mode 100644
> index 000000000000..c82aaf0c1597
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> @@ -0,0 +1,2019 @@
> +/*
> + * cxd2880_top.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__

Same comments as on other patches: use SPDX and dev_foo() for printing 
messages.

> +
> +#include <linux/spi/spi.h>
> +
> +#include "dvb_frontend.h"
> +#include "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 -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return -EPERM;

As pointed previously, please check the returned values on this
patch.

> +
> +	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 -EBUSY;
> +	}
> +
> +	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 -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +	}
> +	*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 -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +
> +	*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 -EBUSY;
> +
> +	if (period_exp == 11)
> +		*post_bit_count = 3342336;
> +	else
> +		*post_bit_count = (1U << period_exp) * 204 * 81;
> +
> +	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 -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +	}
> +
> +	*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 -EBUSY;
> +
> +	n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
> +
> +	if (*post_bit_err > ((1U << period_exp) * n_bch))
> +		return -EBUSY;
> +
> +	*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 -EPERM;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +
> +	*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 -EBUSY;
> +
> +	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 -EPERM;
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return -EPERM;
> +
> +	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 -EBUSY;
> +
> +	*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 -EBUSY;
> +
> +	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 = (u16)(((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 = (u16)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)
> +{
> +	int ret;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!ber)) {
> +		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_pre_rsber(&priv->tnrdmd,
> +							ber);
> +		/* x100 to change unit.(10^7 -> 10^9) */
> +		*ber *= 100;
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(&priv->tnrdmd,
> +							  ber);
> +	} 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_set_ber_per_period_t(struct dvb_frontend *fe)
> +{
> +	int ret;
> +	struct dtv_frontend_properties *c;
> +	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;
> +	c = &fe->dtv_property_cache;
> +	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 dtv_frontend_properties *c;
> +	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;
> +	c = &fe->dtv_property_cache;
> +	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 -EPERM;
> +
> +	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 -EOPNOTSUPP;
> +	}
> +
> +	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);
> +
> +	ret = cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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 -EPERM;
> +
> +	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 -EOPNOTSUPP;
> +	}
> +
> +	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);
> +
> +	ret = cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +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)
> +{
> +	struct cxd2880_priv *priv = NULL;
> +	int ret;
> +
> +	if ((!fe) || (!props)) {
> +		pr_err("invalid arg.");
> +		return -EINVAL;
> +	}
> +
> +	priv = fe->demodulator_priv;
> +
> +	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;
> +
> +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);
> +
> +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,
> +};
> +
> +MODULE_DESCRIPTION(
> +"Sony CXD2880 DVB-T2/T tuner + demodulator drvier");
> +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
> +MODULE_LICENSE("GPL v2");



Thanks,
Mauro

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

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (12 preceding siblings ...)
  2017-11-16  1:07 ` [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Takiguchi, Yasunari
@ 2017-12-13 19:36 ` Mauro Carvalho Chehab
  2017-12-14  9:59   ` Takiguchi, Yasunari
  13 siblings, 1 reply; 43+ messages in thread
From: Mauro Carvalho Chehab @ 2017-12-13 19:36 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: akpm, linux-kernel, devicetree, linux-media, tbird20d,
	frowand.list, Masayuki.Yamamoto, Hideki.Nozawa, Kota.Yonezawa,
	Toshihiko.Matsumoto, Satoshi.C.Watanabe, Bird, Timothy

Em Fri, 13 Oct 2017 14:46:35 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> Hi,
> 
> This is the patch series (version 4) of Sony CXD2880 DVB-T2/T tuner + 
> demodulator driver.The driver supports DVB-API and interfaces through 
> SPI.
> 
> We have tested the driver on Raspberry Pi 3 and got picture and sound 
> from a media player.
> 
> The change history of this patch series is as below.

Finally had time to review this patch series. Sorry for taking so long.
4Q is usually very busy.

I answered each patch with comments. There ones I didn't comment
(patches 1, 4 and 8-12) is because I didn't see anything wrong,
except for the notes I did on other patches, mainly:
	- they lack SPDX license text;
	- the error codes need to review.

Additionally, on patches 8-11, I found weird to not found any
64 bits division, but I noticed some code there that it seemed
to actually be doing such division using some algorithm to make
them work on 32 bits machine. If so, please replace them by
do_div64(), as it makes clearer about what's been doing, and it
provides better performance when compiled on 64 bit Kernels (as
they become just a DIV asm operation).

Regards,
Mauro

Thanks,
Mauro

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

* RE: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-12-13 19:36 ` Mauro Carvalho Chehab
@ 2017-12-14  9:59   ` Takiguchi, Yasunari
  2017-12-14 10:55     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-12-14  9:59 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: akpm, linux-kernel, devicetree, linux-media, tbird20d,
	frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Bird, Timothy, Takiguchi, Yasunari

Dear Mauro

Thanks for your review.

We will refer to your comments and consider how to respond for them.
I want to confirm one thing about  SPDX license text

We will add SPDX license text to our files, 
Is it necessary to add SPDX not only .c .h Makefile but also Kconfig?
When I checked current files in driver/media, there is no Kconfig file which has SPDX.

Best Regards,
Takiguchi

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

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-12-14  9:59   ` Takiguchi, Yasunari
@ 2017-12-14 10:55     ` Mauro Carvalho Chehab
  2017-12-14 14:25       ` Philippe Ombredanne
  0 siblings, 1 reply; 43+ messages in thread
From: Mauro Carvalho Chehab @ 2017-12-14 10:55 UTC (permalink / raw)
  To: Takiguchi, Yasunari
  Cc: akpm, linux-kernel, devicetree, linux-media, tbird20d,
	frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Bird, Timothy

Em Thu, 14 Dec 2017 09:59:32 +0000
"Takiguchi, Yasunari" <Yasunari.Takiguchi@sony.com> escreveu:

> Dear Mauro
> 
> Thanks for your review.
> 
> We will refer to your comments and consider how to respond for them.
> I want to confirm one thing about  SPDX license text
> 
> We will add SPDX license text to our files, 
> Is it necessary to add SPDX not only .c .h Makefile but also Kconfig?
> When I checked current files in driver/media, there is no Kconfig file which has SPDX.

SPDX is a new requirement that started late on Kernel 4.14 development
cycle (and whose initial changes were merged directly at Linus tree).
Not all existing files have it yet, as identifying the right license
on existing files is a complex task, but if you do a:

	$ git grep SPDX $(find . -name Makefile) $(find . -name Kconfig)

You'll see that lot of such files have it already.

So, yes, please add it to both Makefile and Kconfig.

Thanks,
Mauro

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

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-12-14 10:55     ` Mauro Carvalho Chehab
@ 2017-12-14 14:25       ` Philippe Ombredanne
  2017-12-14 17:32         ` Bird, Timothy
  0 siblings, 1 reply; 43+ messages in thread
From: Philippe Ombredanne @ 2017-12-14 14:25 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Takiguchi, Yasunari, akpm, linux-kernel, devicetree, linux-media,
	tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Bird, Timothy

Dear Mauro,

On Thu, Dec 14, 2017 at 11:55 AM, Mauro Carvalho Chehab
<mchehab@s-opensource.com> wrote:

> SPDX is a new requirement that started late on Kernel 4.14 development
> cycle (and whose initial changes were merged directly at Linus tree).
> Not all existing files have it yet, as identifying the right license
> on existing files is a complex task, but if you do a:
>
>         $ git grep SPDX $(find . -name Makefile) $(find . -name Kconfig)
>
> You'll see that lot of such files have it already.

FWIW, short of having SPDX tags, identifying the right license on
existing files is not a super complex task: if boils down to running
many diffs.

Take the ~60K files in kernel, and about 6K license and notices
reference texts. Then compute a pairwise diff of each of the 60K file
against the 6K reference texts. Repeat the pairwise diff a few more
times, say 10 times, as multiple licenses may appear in any given
kernel file. And keep the diffs that have the fewest
difference/highest similarity with the reference texts as the detected
license. Done!

The only complex thing is that if you have a fast diff that runs at
0.1 millisec end-to-end per diff, you still have 3.6B diffs to do and
this would take about 250 days on one thread. Even with a beefy 250
core CPU, that would still be a day (and quite few kilo watts) . So
the whole trick is to avoid doing a diffs if not really needed. This
is what I do in my scancode-toolkit (that I used/use to help Greg and
Thomas with kernel license scans). Net effect is that on a laptop on 8
threads it takes ~20 minutes to scan a whole kernel using this
diff-based approach and obtain a fairly accurate license detection.
-- 
Cordially
Philippe Ombredanne

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

* RE: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-12-14 14:25       ` Philippe Ombredanne
@ 2017-12-14 17:32         ` Bird, Timothy
  2017-12-14 18:04           ` Mauro Carvalho Chehab
  2017-12-14 18:22           ` Philippe Ombredanne
  0 siblings, 2 replies; 43+ messages in thread
From: Bird, Timothy @ 2017-12-14 17:32 UTC (permalink / raw)
  To: Philippe Ombredanne, Mauro Carvalho Chehab
  Cc: Takiguchi, Yasunari, akpm, linux-kernel, devicetree, linux-media,
	tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)



> -----Original Message-----
> From: Philippe on Thursday, December 14, 2017 6:25 AM
> Dear Mauro,
> 
> On Thu, Dec 14, 2017 at 11:55 AM, Mauro Carvalho Chehab
> <mchehab@s-opensource.com> wrote:
> 
> > SPDX is a new requirement that started late on Kernel 4.14 development
> > cycle (and whose initial changes were merged directly at Linus tree).
> > Not all existing files have it yet, as identifying the right license
> > on existing files is a complex task, but if you do a:
> >
> >         $ git grep SPDX $(find . -name Makefile) $(find . -name Kconfig)
> >
> > You'll see that lot of such files have it already.
> 
> FWIW, short of having SPDX tags, identifying the right license on
> existing files is not a super complex task: if boils down to running
> many diffs.
> 
> Take the ~60K files in kernel, and about 6K license and notices
> reference texts. Then compute a pairwise diff of each of the 60K file
> against the 6K reference texts. Repeat the pairwise diff a few more
> times, say 10 times, as multiple licenses may appear in any given
> kernel file. And keep the diffs that have the fewest
> difference/highest similarity with the reference texts as the detected
> license. Done!

You can't do license detection and assignment in this automated fashion - 
at least not generally.

Even a single word of difference between the notice in the source
code and the reference license notice or text may have legal implications
that are not conveyed by the simplified SPDX tag.  When differences are
found, we're going to have to kick the discrepancies to a human for review.
This is especially true for files with multiple licenses.

For a work of original authorship, or a single copyright holder, the author
or copyright holder may be able to change the notice or text, or gloss
over any difference from the reference text, and make the SPDX  assignment
(or even change the license, if they want).  This would apply to something
new like this Sony driver.  However, for code that is already in the kernel
tree, with likely multiple contributors, the legal situation gets a little
more murky.

I suspect the vast majority of the ~60k files will probably fall neatly into an
SPDX category, but I'm guessing a fair number (maybe hundreds) will require
some review and discussion.

 -- Tim



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

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-12-14 17:32         ` Bird, Timothy
@ 2017-12-14 18:04           ` Mauro Carvalho Chehab
  2017-12-14 18:30             ` Philippe Ombredanne
  2018-01-15  0:49             ` Takiguchi, Yasunari
  2017-12-14 18:22           ` Philippe Ombredanne
  1 sibling, 2 replies; 43+ messages in thread
From: Mauro Carvalho Chehab @ 2017-12-14 18:04 UTC (permalink / raw)
  To: Bird, Timothy
  Cc: Philippe Ombredanne, Takiguchi, Yasunari, akpm, linux-kernel,
	devicetree, linux-media, tbird20d, frowand.list, Yamamoto,
	Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

Em Thu, 14 Dec 2017 17:32:34 +0000
"Bird, Timothy" <Tim.Bird@sony.com> escreveu:

> > -----Original Message-----
> > From: Philippe on Thursday, December 14, 2017 6:25 AM
> > Dear Mauro,
> > 
> > On Thu, Dec 14, 2017 at 11:55 AM, Mauro Carvalho Chehab
> > <mchehab@s-opensource.com> wrote:
> >   
> > > SPDX is a new requirement that started late on Kernel 4.14 development
> > > cycle (and whose initial changes were merged directly at Linus tree).
> > > Not all existing files have it yet, as identifying the right license
> > > on existing files is a complex task, but if you do a:
> > >
> > >         $ git grep SPDX $(find . -name Makefile) $(find . -name Kconfig)
> > >
> > > You'll see that lot of such files have it already.  
> > 
> > FWIW, short of having SPDX tags, identifying the right license on
> > existing files is not a super complex task: if boils down to running
> > many diffs.
> > 
> > Take the ~60K files in kernel, and about 6K license and notices
> > reference texts. Then compute a pairwise diff of each of the 60K file
> > against the 6K reference texts. Repeat the pairwise diff a few more
> > times, say 10 times, as multiple licenses may appear in any given
> > kernel file. And keep the diffs that have the fewest
> > difference/highest similarity with the reference texts as the detected
> > license. Done!  
> 
> You can't do license detection and assignment in this automated fashion - 
> at least not generally.
> 
> Even a single word of difference between the notice in the source
> code and the reference license notice or text may have legal implications
> that are not conveyed by the simplified SPDX tag.  When differences are
> found, we're going to have to kick the discrepancies to a human for review.
> This is especially true for files with multiple licenses.
> 
> For a work of original authorship, or a single copyright holder, the author
> or copyright holder may be able to change the notice or text, or gloss
> over any difference from the reference text, and make the SPDX  assignment
> (or even change the license, if they want).  This would apply to something
> new like this Sony driver.  However, for code that is already in the kernel
> tree, with likely multiple contributors, the legal situation gets a little
> more murky.

Precisely. This is easily fixable when the code author changes the
license text, or when someone from the Company that holds copyrights sends
the SPDX markups (as emails @company can be seen as official e-mails, except
if explicitly noticed otherwise). So, from my side, I'm now requiring
SPDX for new drivers.

However, if someone else is doing the changes, it can be tricky and risky
to pick up the patch, adding my SOB, if not endorsed by the copyright
owners, or by LF legal counseling. So, I prefer to not pick those myself,
except from people I trust.

> I suspect the vast majority of the ~60k files will probably fall neatly into an
> SPDX category, but I'm guessing a fair number (maybe hundreds) will require
> some review and discussion.

Thanks,
Mauro

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

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-12-14 17:32         ` Bird, Timothy
  2017-12-14 18:04           ` Mauro Carvalho Chehab
@ 2017-12-14 18:22           ` Philippe Ombredanne
  1 sibling, 0 replies; 43+ messages in thread
From: Philippe Ombredanne @ 2017-12-14 18:22 UTC (permalink / raw)
  To: Bird, Timothy
  Cc: Mauro Carvalho Chehab, Takiguchi, Yasunari, akpm, linux-kernel,
	devicetree, linux-media, tbird20d, frowand.list, Yamamoto,
	Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

Tim,

On Thu, Dec 14, 2017 at 6:32 PM, Bird, Timothy <Tim.Bird@sony.com> wrote:
>
>
>> -----Original Message-----
>> From: Philippe on Thursday, December 14, 2017 6:25 AM
>> Dear Mauro,
>>
>> On Thu, Dec 14, 2017 at 11:55 AM, Mauro Carvalho Chehab
>> <mchehab@s-opensource.com> wrote:
>>
>> > SPDX is a new requirement that started late on Kernel 4.14 development
>> > cycle (and whose initial changes were merged directly at Linus tree).
>> > Not all existing files have it yet, as identifying the right license
>> > on existing files is a complex task, but if you do a:
>> >
>> >         $ git grep SPDX $(find . -name Makefile) $(find . -name Kconfig)
>> >
>> > You'll see that lot of such files have it already.
>>
>> FWIW, short of having SPDX tags, identifying the right license on
>> existing files is not a super complex task: if boils down to running
>> many diffs.
>>
>> Take the ~60K files in kernel, and about 6K license and notices
>> reference texts. Then compute a pairwise diff of each of the 60K file
>> against the 6K reference texts. Repeat the pairwise diff a few more
>> times, say 10 times, as multiple licenses may appear in any given
>> kernel file. And keep the diffs that have the fewest
>> difference/highest similarity with the reference texts as the detected
>> license. Done!
>
> You can't do license detection and assignment in this automated fashion -
> at least not generally.
>
> Even a single word of difference between the notice in the source
> code and the reference license notice or text may have legal implications
> that are not conveyed by the simplified SPDX tag.  When differences are
> found, we're going to have to kick the discrepancies to a human for review.
> This is especially true for files with multiple licenses.
>
> For a work of original authorship, or a single copyright holder, the author
> or copyright holder may be able to change the notice or text, or gloss
> over any difference from the reference text, and make the SPDX  assignment
> (or even change the license, if they want).  This would apply to something
> new like this Sony driver.  However, for code that is already in the kernel
> tree, with likely multiple contributors, the legal situation gets a little
> more murky.
>
> I suspect the vast majority of the ~60k files will probably fall neatly into an
> SPDX category, but I'm guessing a fair number (maybe hundreds) will require
> some review and discussion.

You are completely and 100% right. I was just describing the mechanics
of the license detection side. The actual process has been and always
will be scan then review carefully and then discuss. There is no sane
automated tool that could do it all for sure.

As a funny side note, there are over 80+ licenses in the kernel and
there is (or rather was before starting adding SPDX tags) 1000+
different license notices and over 700+ variations of "this file in
under the GPL"... This starts to diminish a bit with the addition of
SPDX tags and eventually most or all boilerplate could be removed over
time with reviews and discussions, IMHO for the better: I will then be
able to trash my tool and use a good ole grep instead ;)

-- 
Cordially
Philippe Ombredanne

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

* Re: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-12-14 18:04           ` Mauro Carvalho Chehab
@ 2017-12-14 18:30             ` Philippe Ombredanne
  2018-01-15  0:49             ` Takiguchi, Yasunari
  1 sibling, 0 replies; 43+ messages in thread
From: Philippe Ombredanne @ 2017-12-14 18:30 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Bird, Timothy, Takiguchi, Yasunari, akpm, linux-kernel,
	devicetree, linux-media, tbird20d, frowand.list, Yamamoto,
	Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

On Thu, Dec 14, 2017 at 7:04 PM, Mauro Carvalho Chehab
<mchehab@s-opensource.com> wrote:
> Em Thu, 14 Dec 2017 17:32:34 +0000
> "Bird, Timothy" <Tim.Bird@sony.com> escreveu:
>
>> > -----Original Message-----
>> > From: Philippe on Thursday, December 14, 2017 6:25 AM
>> > Dear Mauro,
>> >
>> > On Thu, Dec 14, 2017 at 11:55 AM, Mauro Carvalho Chehab
>> > <mchehab@s-opensource.com> wrote:
>> >
>> > > SPDX is a new requirement that started late on Kernel 4.14 development
>> > > cycle (and whose initial changes were merged directly at Linus tree).
>> > > Not all existing files have it yet, as identifying the right license
>> > > on existing files is a complex task, but if you do a:
>> > >
>> > >         $ git grep SPDX $(find . -name Makefile) $(find . -name Kconfig)
>> > >
>> > > You'll see that lot of such files have it already.
>> >
>> > FWIW, short of having SPDX tags, identifying the right license on
>> > existing files is not a super complex task: if boils down to running
>> > many diffs.
>> >
>> > Take the ~60K files in kernel, and about 6K license and notices
>> > reference texts. Then compute a pairwise diff of each of the 60K file
>> > against the 6K reference texts. Repeat the pairwise diff a few more
>> > times, say 10 times, as multiple licenses may appear in any given
>> > kernel file. And keep the diffs that have the fewest
>> > difference/highest similarity with the reference texts as the detected
>> > license. Done!
>>
>> You can't do license detection and assignment in this automated fashion -
>> at least not generally.
>>
>> Even a single word of difference between the notice in the source
>> code and the reference license notice or text may have legal implications
>> that are not conveyed by the simplified SPDX tag.  When differences are
>> found, we're going to have to kick the discrepancies to a human for review.
>> This is especially true for files with multiple licenses.
>>
>> For a work of original authorship, or a single copyright holder, the author
>> or copyright holder may be able to change the notice or text, or gloss
>> over any difference from the reference text, and make the SPDX  assignment
>> (or even change the license, if they want).  This would apply to something
>> new like this Sony driver.  However, for code that is already in the kernel
>> tree, with likely multiple contributors, the legal situation gets a little
>> more murky.
>
> Precisely. This is easily fixable when the code author changes the
> license text, or when someone from the Company that holds copyrights sends
> the SPDX markups (as emails @company can be seen as official e-mails, except
> if explicitly noticed otherwise). So, from my side, I'm now requiring
> SPDX for new drivers.
>
> However, if someone else is doing the changes, it can be tricky and risky
> to pick up the patch, adding my SOB, if not endorsed by the copyright
> owners, or by LF legal counseling. So, I prefer to not pick those myself,
> except from people I trust.

Exactly, and this why --after the first batch that Greg pushed and
Linus pulled and that had been carefully reviewed--, I am trying to
gently nit the submitters of new patches, one at a time to use the new
SPDX tags. Eventually if I can find the time, I could also submit some
bigger patches to add SPDX tags to a bunch of files at once but that
would have to be organized in small batches by copyright holder and
these would be only RFCs until reviewed by and agreed to by the actual
copyright holders.

-- 
Cordially
Philippe Ombredanne

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

* RE: [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2017-12-13 17:54   ` Mauro Carvalho Chehab
@ 2017-12-20  0:54     ` Takiguchi, Yasunari
  0 siblings, 0 replies; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-12-20  0:54 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Takiguchi, Yasunari

Hi, Mauro.

> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
> 
> It would be better to use dev_foo() debug macros instead of
> pr_foo() ones.

I got comment for this previous version patch as below
--------------------------------------------------------------------------------------
The best would be to se dev_err() & friends for printing messages, 
as they print the device's name as filled at struct device.
If you don't use, please add a define that will print the name at the logs, like:

  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

either at the begining of the driver or at some header file.

Btw, I'm noticing that you're also using dev_err() on other places of the code. 
Please standardize. OK, on a few places, you may still need to use pr_err(), 
if you need to print a message before initializing struct device, but I suspect that you can init
--------------------------------------------------------------------------------------

You pointed out here before. Because dev_foo () and pr_foo () were mixed.
We standardize with pr_foo() because the logs is outputted before getting the device structure.
Is it better to use dev_foo() where we can use it?


> > +static int cxd2880_stop_feed(struct dvb_demux_feed *feed) {
> > +	int i = 0;
> > +	int ret;
> > +	struct dvb_demux *demux = NULL;
> > +	struct cxd2880_dvb_spi *dvb_spi = NULL;
> > +
> > +	if (!feed) {
> > +		pr_err("invalid arg\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	demux = feed->demux;
> > +	if (!demux) {
> > +		pr_err("feed->demux is NULL\n");
> > +		return -EINVAL;
> > +	}
> > +	dvb_spi = demux->priv;
> > +
> > +	if (!dvb_spi->feed_count) {
> > +		pr_err("no feed is started\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (feed->pid == 0x2000) {
> > +		/*
> > +		 * Special PID case.
> > +		 * Number of 0x2000 feed request was stored
> > +		 * in dvb_spi->all_pid_feed_count.
> > +		 */
> > +		if (dvb_spi->all_pid_feed_count <= 0) {
> > +			pr_err("PID %d not found.\n", feed->pid);
> > +			return -EINVAL;
> > +		}
> > +		dvb_spi->all_pid_feed_count--;
> > +	} else {
> > +		struct cxd2880_pid_filter_config cfgtmp;
> > +
> > +		cfgtmp = dvb_spi->filter_config;
> > +
> > +		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> > +			if (feed->pid == cfgtmp.pid_config[i].pid &&
> > +			    cfgtmp.pid_config[i].is_enable != 0) {
> > +				cfgtmp.pid_config[i].is_enable = 0;
> > +				cfgtmp.pid_config[i].pid = 0;
> > +				pr_debug("removed PID %d from #%d\n",
> > +					 feed->pid, i);
> > +				break;
> > +			}
> > +		}
> > +		dvb_spi->filter_config = cfgtmp;
> > +
> > +		if (i == CXD2880_MAX_FILTER_SIZE) {
> > +			pr_err("PID %d not found\n", feed->pid);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	ret = cxd2880_update_pid_filter(dvb_spi,
> > +					&dvb_spi->filter_config,
> > +					dvb_spi->all_pid_feed_count >
> 0);
> > +	dvb_spi->feed_count--;
> > +
> > +	if (dvb_spi->feed_count == 0) {
> > +		int ret_stop = 0;
> > +
> > +		ret_stop =
> kthread_stop(dvb_spi->cxd2880_ts_read_thread);
> > +		if (ret_stop) {
> > +			pr_err("'kthread_stop failed. (%d)\n",
> ret_stop);
> > +			ret = ret_stop;
> > +		}
> > +		kfree(dvb_spi->ts_buf);
> > +		dvb_spi->ts_buf = NULL;
> > +	}
> > +
> > +	pr_debug("stop feed ok.(count %d)\n", dvb_spi->feed_count);
> > +
> > +	return ret;
> > +}
> 
> I have the feeling that I've seen the code above before at the dvb core.
> Any reason why don't use the already-existing code at dvb_demux.c &
> friends?

The CXD2880 HW PID filter is used to decrease the amount of TS data transferred via limited bit rate SPI interface.

Thanks,
Takiguchi

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

* RE: [PATCH v4 05/12] [media] cxd2880: Add tuner part of the driver
  2017-12-13 18:40   ` Mauro Carvalho Chehab
@ 2017-12-20  1:01     ` Takiguchi, Yasunari
  0 siblings, 0 replies; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-12-20  1:01 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Takiguchi, Yasunari

Hi, Mauro


> > +	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 -EBUSY;
> 
> I don't know anything about this hardware, but it sounds weird to return
> -EBUSY here, except if the hardware reached a permanent busy condition,
> and would require some sort of reset to work again.
> 
> As this is in the middle of lots of things, I *suspect* that this is
> not the case.
> 
> If I'm right, and this is just a transitory solution that could happen
> for a limited amount of time, e. g. if what's there at data[0] is a flag
> saying that the device didn't finish the last operation yet, maybe the
> best would be to do something like:
> 
> 	for (i = 0; i < 10; i++) {
> 		ret = tnr_dmd->io->read_regs(tnr_dmd->io,
> 					     CXD2880_IO_TGT_SYS,
> 					     0x10, data, 1);
> 		if (ret)
> 			return ret;
> 		if (data[0] & 0x01)
> 			break;
> 		msleep(10);
> 	}
> 	if (!(data[0] & 0x01))
> 		return -EBUSY;
> 
> > +
> > +	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 -EBUSY;
> 
> Same here and on similar places.

As the hardware specification, It is abnormal if certain register doesn't become 1 even if sleep time passes.
Perhaps it should not be return EBUSY.
We will reconsider error code.

Thanks,
Takiguchi

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

* RE: [PATCH v4 06/12] [media] cxd2880: Add integration layer for the driver
  2017-12-13 19:13   ` Mauro Carvalho Chehab
@ 2017-12-20  1:06     ` Takiguchi, Yasunari
  0 siblings, 0 replies; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-12-20  1:06 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Takiguchi, Yasunari

Hi, Mauro.

> >
> > These functions monitor the driver and watch for task completion.
> > This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.
> 
> If I understand well, the goal here is to have thread that would be waking
> up from time to time, right? Just use the infrastructure that the Kernel
> has for it, like a kthread, or timer_setup() & friends.
> 
> Take a look at include/linux/timer.h, and just use what's already defined.

This code is initialize process.
Therefore, it is executed only once and it will not execute other processing at the same time.
We think that the current implementation is enough.
What do you think?
furthermore, we will modify this code by using ktime_foo().

Thanks,
Takiguchi

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

* RE: [PATCH v4 07/12] [media] cxd2880: Add top level of the driver
  2017-12-13 19:25   ` Mauro Carvalho Chehab
@ 2017-12-20  1:18     ` Takiguchi, Yasunari
  0 siblings, 0 replies; 43+ messages in thread
From: Takiguchi, Yasunari @ 2017-12-20  1:18 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Takiguchi, Yasunari

Hi, Mauro

> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
> 
> Same comments as on other patches: use SPDX and dev_foo() for printing
> messages.

About printing messages pr_fmt, I also replied a comment to [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface.
Please refer to the comment.

Thanks,
Takiguchi

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

* RE: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-12-14 18:04           ` Mauro Carvalho Chehab
  2017-12-14 18:30             ` Philippe Ombredanne
@ 2018-01-15  0:49             ` Takiguchi, Yasunari
  2018-01-15  1:27               ` Takiguchi, Yasunari
  1 sibling, 1 reply; 43+ messages in thread
From: Takiguchi, Yasunari @ 2018-01-15  0:49 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Bird, Timothy
  Cc: Philippe Ombredanne, akpm, linux-kernel, devicetree, linux-media,
	tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Takiguchi, Yasunari

Dear Mauro

I am creating patch files for version 5 of our driver.
I need to add our driver information into /drivers/media/dvb-frontends/Kconfig.
This Kconfig has no SPDX license Identifier now.
Is it unnecessary for us to add SPDX into  /drivers/media/dvb-frontends/Kconfig?
(Should we add our driver information only into /drivers/media/dvb-frontends/Kconfig?)

Regards,
Takiguchi

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

* RE: [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2018-01-15  0:49             ` Takiguchi, Yasunari
@ 2018-01-15  1:27               ` Takiguchi, Yasunari
  0 siblings, 0 replies; 43+ messages in thread
From: Takiguchi, Yasunari @ 2018-01-15  1:27 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Bird, Timothy
  Cc: Philippe Ombredanne, akpm, linux-kernel, devicetree, linux-media,
	tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	Takiguchi, Yasunari

Dear Mauro

> 
> I am creating patch files for version 5 of our driver.
> I need to add our driver information into
> /drivers/media/dvb-frontends/Kconfig.
> This Kconfig has no SPDX license Identifier now.
> Is it unnecessary for us to add SPDX into
> /drivers/media/dvb-frontends/Kconfig?
> (Should we add our driver information only into
> /drivers/media/dvb-frontends/Kconfig?)

Additionally, 
I need to add our driver information into 
driver/media/spi/Makefile and driver/media/spi/Kconfig,
But these Makefile and Kconfig also has 
no SPDX license Identifier now.
Should I keep no SPDX for them also?

Best Regards,
Takiguchi

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

end of thread, other threads:[~2018-01-15  1:27 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-13  5:46 [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
2017-10-13  5:58 ` [PATCH v4 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
2017-10-13  5:59 ` [PATCH v4 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Yasunari.Takiguchi
2017-10-13  8:44   ` Honza Petrouš
2017-10-13  9:10     ` Takiguchi, Yasunari
2017-10-13 10:26       ` Honza Petrouš
2017-10-16  1:35         ` Takiguchi, Yasunari
2017-12-13 17:54   ` Mauro Carvalho Chehab
2017-12-20  0:54     ` Takiguchi, Yasunari
2017-10-13  6:02 ` [PATCH v4 03/12] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
2017-12-13 18:02   ` Mauro Carvalho Chehab
2017-10-13  6:05 ` [PATCH v4 04/12] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
2017-10-13  6:07 ` [PATCH v4 05/12] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
2017-12-13 18:40   ` Mauro Carvalho Chehab
2017-12-20  1:01     ` Takiguchi, Yasunari
2017-10-13  6:08 ` [PATCH v4 06/12] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
2017-12-03 18:23   ` Sean Young
2017-12-13 19:13   ` Mauro Carvalho Chehab
2017-12-20  1:06     ` Takiguchi, Yasunari
2017-10-13  6:09 ` [PATCH v4 07/12] [media] cxd2880: Add top level of " Yasunari.Takiguchi
2017-12-03 22:59   ` Sean Young
2017-12-05 11:47     ` Takiguchi, Yasunari
2017-12-13 19:25   ` Mauro Carvalho Chehab
2017-12-20  1:18     ` Takiguchi, Yasunari
2017-10-13  6:10 ` [PATCH v4 08/12] [media] cxd2880: Add DVB-T control functions " Yasunari.Takiguchi
2017-10-13  6:11 ` [PATCH v4 09/12] [media] cxd2880: Add DVB-T monitor functions Yasunari.Takiguchi
2017-10-13  6:13 ` [PATCH v4 10/12] [media] cxd2880: Add DVB-T2 control functions for the driver Yasunari.Takiguchi
2017-10-13  6:15 ` [PATCH v4 11/12] [media] cxd2880: Add DVB-T2 monitor functions Yasunari.Takiguchi
2017-10-13  6:17 ` [PATCH v4 12/12] [media] cxd2880: Add all Makefile, Kconfig files and Update MAINTAINERS file for the driver Yasunari.Takiguchi
2017-11-16  1:07 ` [PATCH v4 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Takiguchi, Yasunari
2017-11-22  4:17   ` Takiguchi, Yasunari
2017-11-29  7:38     ` Mauro Carvalho Chehab
2017-11-29  8:21       ` Takiguchi, Yasunari
2017-12-13 19:36 ` Mauro Carvalho Chehab
2017-12-14  9:59   ` Takiguchi, Yasunari
2017-12-14 10:55     ` Mauro Carvalho Chehab
2017-12-14 14:25       ` Philippe Ombredanne
2017-12-14 17:32         ` Bird, Timothy
2017-12-14 18:04           ` Mauro Carvalho Chehab
2017-12-14 18:30             ` Philippe Ombredanne
2018-01-15  0:49             ` Takiguchi, Yasunari
2018-01-15  1:27               ` Takiguchi, Yasunari
2017-12-14 18:22           ` Philippe Ombredanne

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).