linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
@ 2018-01-18  8:40 Yasunari.Takiguchi
  2018-01-18  8:43 ` [PATCH v5 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
                   ` (12 more replies)
  0 siblings, 13 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:40 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 5) 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 V5
(1)Using SPDX-License-Identifier instead of 
   repeating the copyright notes.

(2)Removed a file.
    This below file is removed because we changed stop_watch 
   function to ktime.

   [PATCH v5 03/12]
       drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
          -removed

(3)The detail change items of each files are as below.
    [PATCH v5 02/12]
       Using SPDX-License-Identifier
       drivers/media/spi/cxd2880-spi.c
         -modified typo about "ivnalid" -> "invalid"
         -modified typo about "drvier" -> "driver"
         -removed unnecessary if() 
         -modified return error code
         -reduction of valiable names
         -removed unnecessary parentheses 
         -changed members of struct cxd2880_ts_buf_info

    [PATCH v5 03/12]
       Using SPDX-License-Identifier
       drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
         -modified return not to use ret parameter. 
       drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
         -removed unnecessary parentheses
       drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
         -removed function proto type about cxd2880_stopwatch
         -removed CXD2880_ARG_UNUSED
       #drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
         -cxd2880_stopwatch_port.c file was removed from V5.

    [PATCH v5 04/12]
       Using SPDX-License-Identifier
       drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
         -modified return error code
         -removed unnecessary parentheses 
       drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
         -removed unnecessary parentheses

    [PATCH v5 05/12]
       Using SPDX-License-Identifier
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
         -removed unnecessary if()
         -modified if (ret) return ret;
         -modified return error code
         -removed unnecessary parentheses
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
         -removed struct cxd2880_tnrdmd_ts_buf_info
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
         -removed unnecessary if()        
         -modified return error code
         -removed unnecessary parentheses
         -removed cxd2880_tnrdmd_mon_ts_buf_info() 
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
         -removed cxd2880_tnrdmd_mon_ts_buf_info()  
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
          -updated version information

    [PATCH v5 06/12]
       Using SPDX-License-Identifier
       drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
         -removed unnecessary if() 
         -changed timer function from stop_watch to ktime
   
    [PATCH v5 07/12]
       Using SPDX-License-Identifier
       drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
         -changed define position of cxd2880_dvbt_t2_ops
         -modified typo about "drvier" -> "driver"
         -removed unnecessary cast
         -removed unnecessary if() 
         -modified return error code
         -removed unnecessary parentheses 
         -modified for "Lines should not end with a '(' "
         -modified to return constant 0 from read_ber function

    [PATCH v5 08/12]
       Using SPDX-License-Identifier
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
         -removed unnecessary if()        
         -modified return error code
         -removed unnecessary parentheses 

    [PATCH v5 09/12]
       Using SPDX-License-Identifier
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
         -removed unnecessary if()        
         -modified return error code
         -removed unnecessary parentheses 
         -modified for "Lines should not end with a '(' "
         -removed unnecessary functions
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
         -removed unnecessary functions

    [PATCH v5 10/12]
       Using SPDX-License-Identifier
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
         -removed unnecessary if()        
         -modified return error code
         -removed unnecessary parentheses 

    [PATCH v5 11/12]
       Using SPDX-License-Identifier
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
         -removed unnecessary if()        
         -modified return error code
         -removed unnecessary parentheses 
         -modified for "Lines should not end with a '(' "
         -removed unnecessary functions
       drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
         -removed unnecessary functions

    [PATCH v5 12/12]
       drivers/media/dvb-frontends/cxd2880/Makefile
          -Using SPDX-License-Identifier
          -removed cxd2880_stopwatch_port.o
       drivers/media/dvb-frontends/cxd2880/Kconfig
          -Using SPDX-License-Identifier

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
          -removinteed 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                                     | 670 ++++++++++++++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880.h                       | 29 ++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c                | 21 +++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h                | 19 +++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c                    | 66 ++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h                    | 54 ++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c             | 129 +++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h             |  23 ++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h                   |  34 ++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c            | 113 ++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h            |  26 +++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h                   |   29 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c                | 3519 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h                |  365 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h |   12 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c            |  150 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h            |   29 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c                 | 72 ++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h                 | 27 ++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c                   | 1954 +++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h                  |  74 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c           | 919 +++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h           |  45 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c       | 775 +++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h       |  77 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h                 |  385 +++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c          | 1217 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h          |   65 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c      | 1878 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h      |  135 ++
 MAINTAINERS                                                         |  9 +++++++++
 drivers/media/dvb-frontends/Kconfig                                 |  2 ++
 drivers/media/dvb-frontends/Makefile                                |  1 +
 drivers/media/dvb-frontends/cxd2880/Kconfig                         |  8 ++++++++
 drivers/media/dvb-frontends/cxd2880/Makefile                        | 19 +++++++++++++++++++
 drivers/media/spi/Kconfig                                           | 14 ++++++++++++++
 drivers/media/spi/Makefile                                          |  5 +++++

 38 files changed, 12983 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_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] 17+ messages in thread

* [PATCH v5 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
@ 2018-01-18  8:43 ` Yasunari.Takiguchi
  2018-01-18  8:46 ` [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Yasunari.Takiguchi
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:43 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>

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

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

* [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
  2018-01-18  8:43 ` [PATCH v5 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
@ 2018-01-18  8:46 ` Yasunari.Takiguchi
  2018-03-07 10:15   ` Mauro Carvalho Chehab
  2018-01-18  8:47 ` [PATCH v5 03/12] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:46 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/spi/cxd2880-spi.c
      -modified typo about "ivnalid" -> "invalid"
      -modified typo about "drvier" -> "driver"
      -removed unnecessary if() 
      -modified return error code
      -reduction of valiable names
      -removed unnecessary parentheses 
      -changed members of struct cxd2880_ts_buf_info

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 | 670 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 670 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..857e4c0d7a92
--- /dev/null
+++ b/drivers/media/spi/cxd2880-spi.c
@@ -0,0 +1,670 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880-spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI adapter
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+#include <linux/spi/spi.h>
+#include <linux/ktime.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_PKT 300
+
+struct cxd2880_ts_buf_info {
+	u8 read_ready:1;
+	u8 almost_full:1;
+	u8 almost_empty:1;
+	u8 overflow:1;
+	u8 underflow:1;
+	u16 pkt_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;
+
+	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);
+
+	return spi_sync(spi, &msg);
+}
+
+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;
+	data[2] = packet_num;
+
+	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->pkt_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("invalid 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);
+	} 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);
+	}
+
+	return ret;
+}
+
+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;
+	ktime_t start;
+	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 = ktime_get();
+	while (!kthread_should_stop()) {
+		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.pkt_num > MAX_TRANS_PKT) {
+			for (i = 0; i < info.pkt_num / MAX_TRANS_PKT; i++) {
+				cxd2880_spi_read_ts(dvb_spi->spi,
+						    dvb_spi->ts_buf,
+						    MAX_TRANS_PKT);
+				dvb_dmx_swfilter(&dvb_spi->demux,
+						 dvb_spi->ts_buf,
+						 MAX_TRANS_PKT * 188);
+			}
+			start = ktime_get();
+		} else if ((info.pkt_num > 0) &&
+			   (ktime_to_ms(ktime_sub(ktime_get(), start)) >= 500)) {
+			cxd2880_spi_read_ts(dvb_spi->spi,
+					    dvb_spi->ts_buf,
+					    info.pkt_num);
+			dvb_dmx_swfilter(&dvb_spi->demux,
+					 dvb_spi->ts_buf,
+					 info.pkt_num * 188);
+			start = ktime_get();
+		} 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 -EINVAL;
+	}
+
+	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 -EINVAL;
+		}
+		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_PKT * 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 + demod driver SPI adapter");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
-- 
2.15.1

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

* [PATCH v5 03/12] [media] cxd2880: Add common files for the driver
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
  2018-01-18  8:43 ` [PATCH v5 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
  2018-01-18  8:46 ` [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Yasunari.Takiguchi
@ 2018-01-18  8:47 ` Yasunari.Takiguchi
  2018-01-18  8:48 ` [PATCH v5 04/12] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:47 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
      -modified return not to use ret parameter. 
   drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
      -removed unnecessary parentheses
   drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
      -removed function proto type about cxd2880_stopwatch
      -removed CXD2880_ARG_UNUSED
   #drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
      -cxd2880_stopwatch_port.c file was removed from V5.

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      | 29 ++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_common.c   | 21 +++++++
 .../media/dvb-frontends/cxd2880/cxd2880_common.h   | 19 +++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c   | 66 ++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h   | 54 ++++++++++++++++++
 5 files changed, 189 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

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
new file mode 100644
index 000000000000..4ea3510aab66
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_H
+#define CXD2880_H
+
+struct cxd2880_config {
+	struct spi_device *spi;
+	struct mutex *spi_mutex; /* For SPI access exclusive control */
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CXD2880)
+extern struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+					struct cxd2880_config *cfg);
+#else
+static inline struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+					struct cxd2880_config *cfg)
+{
+	pr_warn("%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_CXD2880 */
+
+#endif /* CXD2880_H */
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..d6f5af6609c1
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_common.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_common.h"
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen)
+{
+	if (!bitlen || bitlen >= 32)
+		return (int)value;
+
+	if (value & (u32)(1 << (bitlen - 1)))
+		return (int)(GENMASK(31, bitlen) | value);
+	else
+		return (int)(GENMASK(bitlen - 1, 0) & value);
+}
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..b05bce71ab35
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_common.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_COMMON_H
+#define CXD2880_COMMON_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen);
+
+#endif
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..9d932bccfa6c
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_io.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_io.h"
+
+int cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+				    enum cxd2880_io_tgt tgt,
+				    u8 sub_address, u8 data)
+{
+	if (!io)
+		return -EINVAL;
+
+	return io->write_regs(io, tgt, sub_address, &data, 1);
+}
+
+int cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+			    enum cxd2880_io_tgt tgt,
+			    u8 sub_address, u8 data, u8 mask)
+{
+	int ret;
+
+	if (!io)
+		return -EINVAL;
+
+	if (mask == 0x00)
+		return 0;
+
+	if (mask != 0xff) {
+		u8 rdata = 0x00;
+
+		ret = io->read_regs(io, tgt, sub_address, &rdata, 1);
+		if (ret)
+			return ret;
+
+		data = (data & mask) | (rdata & (mask ^ 0xff));
+	}
+
+	return io->write_reg(io, tgt, sub_address, data);
+}
+
+int cxd2880_io_write_multi_regs(struct cxd2880_io *io,
+			     enum cxd2880_io_tgt tgt,
+			     const struct cxd2880_reg_value reg_value[],
+			     u8 size)
+{
+	int ret;
+	int i;
+
+	if (!io)
+		return -EINVAL;
+
+	for (i = 0; i < size ; i++) {
+		ret = io->write_reg(io, tgt, reg_value[i].addr,
+				    reg_value[i].value);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
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..ba550278881d
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_io.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_IO_H
+#define CXD2880_IO_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_io_tgt {
+	CXD2880_IO_TGT_SYS,
+	CXD2880_IO_TGT_DMD
+};
+
+struct cxd2880_reg_value {
+	u8 addr;
+	u8 value;
+};
+
+struct cxd2880_io {
+	int (*read_regs)(struct cxd2880_io *io,
+			 enum cxd2880_io_tgt tgt, u8 sub_address,
+			 u8 *data, u32 size);
+	int (*write_regs)(struct cxd2880_io *io,
+			  enum cxd2880_io_tgt tgt, u8 sub_address,
+			  const u8 *data, u32 size);
+	int (*write_reg)(struct cxd2880_io *io,
+			 enum cxd2880_io_tgt tgt, u8 sub_address,
+			 u8 data);
+	void *if_object;
+	u8 i2c_address_sys;
+	u8 i2c_address_demod;
+	u8 slave_select;
+	void *user;
+};
+
+int cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+				    enum cxd2880_io_tgt tgt,
+				    u8 sub_address, u8 data);
+
+int cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+			    enum cxd2880_io_tgt tgt,
+			    u8 sub_address, u8 data, u8 mask);
+
+int cxd2880_io_write_multi_regs(struct cxd2880_io *io,
+				enum cxd2880_io_tgt tgt,
+				const struct cxd2880_reg_value reg_value[],
+				u8 size);
+#endif
-- 
2.15.1

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

* [PATCH v5 04/12] [media] cxd2880: Add spi device IO routines
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (2 preceding siblings ...)
  2018-01-18  8:47 ` [PATCH v5 03/12] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
@ 2018-01-18  8:48 ` Yasunari.Takiguchi
  2018-01-18  8:50 ` [PATCH v5 05/12] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:48 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
      -modified return error code
      -removed unnecessary parentheses 
   drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
      -removed unnecessary parentheses

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      | 129 +++++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_devio_spi.h      |  23 ++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h  |  34 ++++++
 .../dvb-frontends/cxd2880/cxd2880_spi_device.c     | 113 ++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_spi_device.h     |  26 +++++
 5 files changed, 325 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..d2e37c95d748
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_devio_spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_devio_spi.h"
+
+#define BURST_WRITE_MAX 128
+
+static int cxd2880_io_spi_read_reg(struct cxd2880_io *io,
+				   enum cxd2880_io_tgt tgt,
+				   u8 sub_address, u8 *data,
+				   u32 size)
+{
+	int ret;
+	struct cxd2880_spi *spi = NULL;
+	u8 send_data[6];
+	u8 *read_data_top = data;
+
+	if (!io || !io->if_object || !data)
+		return -EINVAL;
+
+	if (sub_address + size > 0x100)
+		return -EINVAL;
+
+	spi = io->if_object;
+
+	if (tgt == CXD2880_IO_TGT_SYS)
+		send_data[0] = 0x0b;
+	else
+		send_data[0] = 0x0a;
+
+	send_data[3] = 0;
+	send_data[4] = 0;
+	send_data[5] = 0;
+
+	while (size > 0) {
+		send_data[1] = sub_address;
+		if (size > 255)
+			send_data[2] = 255;
+		else
+			send_data[2] = size;
+
+		ret =
+		    spi->write_read(spi, send_data, sizeof(send_data),
+				    read_data_top, send_data[2]);
+		if (ret)
+			return ret;
+
+		sub_address += send_data[2];
+		read_data_top += send_data[2];
+		size -= send_data[2];
+	}
+
+	return ret;
+}
+
+static int cxd2880_io_spi_write_reg(struct cxd2880_io *io,
+				    enum cxd2880_io_tgt tgt,
+				    u8 sub_address,
+				    const u8 *data, u32 size)
+{
+	int ret;
+	struct cxd2880_spi *spi = NULL;
+	u8 send_data[BURST_WRITE_MAX + 4];
+	const u8 *write_data_top = data;
+
+	if (!io || !io->if_object || !data)
+		return -EINVAL;
+
+	if (size > BURST_WRITE_MAX)
+		return -EINVAL;
+
+	if (sub_address + size > 0x100)
+		return -EINVAL;
+
+	spi = io->if_object;
+
+	if (tgt == CXD2880_IO_TGT_SYS)
+		send_data[0] = 0x0f;
+	else
+		send_data[0] = 0x0e;
+
+	while (size > 0) {
+		send_data[1] = sub_address;
+		if (size > 255)
+			send_data[2] = 255;
+		else
+			send_data[2] = size;
+
+		memcpy(&send_data[3], write_data_top, send_data[2]);
+
+		if (tgt == CXD2880_IO_TGT_SYS) {
+			send_data[3 + send_data[2]] = 0x00;
+			ret = spi->write(spi, send_data, send_data[2] + 4);
+		} else {
+			ret = spi->write(spi, send_data, send_data[2] + 3);
+		}
+		if (ret)
+			return ret;
+
+		sub_address += send_data[2];
+		write_data_top += send_data[2];
+		size -= send_data[2];
+	}
+
+	return ret;
+}
+
+int cxd2880_io_spi_create(struct cxd2880_io *io,
+			  struct cxd2880_spi *spi, u8 slave_select)
+{
+	if (!io || !spi)
+		return -EINVAL;
+
+	io->read_regs = cxd2880_io_spi_read_reg;
+	io->write_regs = cxd2880_io_spi_write_reg;
+	io->write_reg = cxd2880_io_common_write_one_reg;
+	io->if_object = spi;
+	io->i2c_address_sys = 0;
+	io->i2c_address_demod = 0;
+	io->slave_select = slave_select;
+
+	return 0;
+}
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..27f7cb12fad4
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_devio_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DEVIO_SPI_H
+#define CXD2880_DEVIO_SPI_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_io.h"
+#include "cxd2880_spi.h"
+
+#include "cxd2880_tnrdmd.h"
+
+int cxd2880_io_spi_create(struct cxd2880_io *io,
+			  struct cxd2880_spi *spi,
+			  u8 slave_select);
+
+#endif
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..2be207461847
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_SPI_H
+#define CXD2880_SPI_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_spi_mode {
+	CXD2880_SPI_MODE_0,
+	CXD2880_SPI_MODE_1,
+	CXD2880_SPI_MODE_2,
+	CXD2880_SPI_MODE_3
+};
+
+struct cxd2880_spi {
+	int (*read)(struct cxd2880_spi *spi, u8 *data,
+		    u32 size);
+	int (*write)(struct cxd2880_spi *spi, const u8 *data,
+		     u32 size);
+	int (*write_read)(struct cxd2880_spi *spi,
+			  const u8 *tx_data, u32 tx_size,
+			  u8 *rx_data, u32 rx_size);
+	u32 flags;
+	void *user;
+};
+
+#endif
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..b8cbaa8d7aff
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_spi_device.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <linux/spi/spi.h>
+
+#include "cxd2880_spi_device.h"
+
+static int cxd2880_spi_device_write(struct cxd2880_spi *spi,
+				    const u8 *data, u32 size)
+{
+	struct cxd2880_spi_device *spi_device = NULL;
+	struct spi_message msg;
+	struct spi_transfer tx;
+	int result = 0;
+
+	if (!spi || !spi->user || !data || size == 0)
+		return -EINVAL;
+
+	spi_device = spi->user;
+
+	memset(&tx, 0, sizeof(tx));
+	tx.tx_buf = data;
+	tx.len = size;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&tx, &msg);
+	result = spi_sync(spi_device->spi, &msg);
+
+	if (result < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int cxd2880_spi_device_write_read(struct cxd2880_spi *spi,
+					 const u8 *tx_data,
+					 u32 tx_size,
+					 u8 *rx_data,
+					 u32 rx_size)
+{
+	struct cxd2880_spi_device *spi_device = NULL;
+	int result = 0;
+
+	if (!spi || !spi->user || !tx_data ||
+	    !tx_size || !rx_data || !rx_size)
+		return -EINVAL;
+
+	spi_device = spi->user;
+
+	result = spi_write_then_read(spi_device->spi, tx_data,
+				     tx_size, rx_data, rx_size);
+	if (result < 0)
+		return -EIO;
+
+	return 0;
+}
+
+int
+cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+			      enum cxd2880_spi_mode mode,
+			      u32 speed_hz)
+{
+	int result = 0;
+	struct spi_device *spi = spi_device->spi;
+
+	switch (mode) {
+	case CXD2880_SPI_MODE_0:
+		spi->mode = SPI_MODE_0;
+		break;
+	case CXD2880_SPI_MODE_1:
+		spi->mode = SPI_MODE_1;
+		break;
+	case CXD2880_SPI_MODE_2:
+		spi->mode = SPI_MODE_2;
+		break;
+	case CXD2880_SPI_MODE_3:
+		spi->mode = SPI_MODE_3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spi->max_speed_hz = speed_hz;
+	spi->bits_per_word = 8;
+	result = spi_setup(spi);
+	if (result != 0) {
+		pr_err("spi_setup failed %d\n", result);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+				  struct cxd2880_spi_device *spi_device)
+{
+	if (!spi || !spi_device)
+		return -EINVAL;
+
+	spi->read = NULL;
+	spi->write = cxd2880_spi_device_write;
+	spi->write_read = cxd2880_spi_device_write_read;
+	spi->flags = 0;
+	spi->user = spi_device;
+
+	return 0;
+}
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..05e3a03de3a3
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_spi_device.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_SPI_DEVICE_H
+#define CXD2880_SPI_DEVICE_H
+
+#include "cxd2880_spi.h"
+
+struct cxd2880_spi_device {
+	struct spi_device *spi;
+};
+
+int cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+				  enum cxd2880_spi_mode mode,
+				  u32 speedHz);
+
+int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+				  struct cxd2880_spi_device *spi_device);
+
+#endif /* CXD2880_SPI_DEVICE_H */
-- 
2.15.1

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

* [PATCH v5 05/12] [media] cxd2880: Add tuner part of the driver
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (3 preceding siblings ...)
  2018-01-18  8:48 ` [PATCH v5 04/12] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
@ 2018-01-18  8:50 ` Yasunari.Takiguchi
  2018-01-18  8:51 ` [PATCH v5 06/12] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:50 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
      -removed unnecessary if()
      -modified if (ret) return ret;
      -modified return error code
      -removed unnecessary parentheses
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
      -removed struct cxd2880_tnrdmd_ts_buf_info
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
      -removed unnecessary if()        
      -modified return error code
      -removed unnecessary parentheses
      -removed cxd2880_tnrdmd_mon_ts_buf_info() 
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
      -removed cxd2880_tnrdmd_mon_ts_buf_info()  
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
      -updated version information

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

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

* [PATCH v5 06/12] [media] cxd2880: Add integration layer for the driver
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (4 preceding siblings ...)
  2018-01-18  8:50 ` [PATCH v5 05/12] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
@ 2018-01-18  8:51 ` Yasunari.Takiguchi
  2018-01-18  8:52 ` [PATCH v5 07/12] [media] cxd2880: Add top level of " Yasunari.Takiguchi
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:51 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
      -removed unnecessary if() 
      -changed timer function from stop_watch to ktime

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    | 72 ++++++++++++++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_integ.h    | 27 ++++++++
 2 files changed, 99 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..5302ab0964c1
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_integ.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <linux/ktime.h>
+#include <linux/errno.h>
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_integ.h"
+
+int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	int ret;
+	ktime_t start;
+	u8 cpu_task_completed = 0;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_init1(tnr_dmd);
+	if (ret)
+		return ret;
+
+	start = ktime_get();
+
+	while (1) {
+		ret =
+		    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+						     &cpu_task_completed);
+		if (ret)
+			return ret;
+
+		if (cpu_task_completed)
+			break;
+
+		if (ktime_to_ms(ktime_sub(ktime_get(), start)) >
+					CXD2880_TNRDMD_WAIT_INIT_TIMEOUT)
+			return -ETIMEDOUT;
+
+		usleep_range(CXD2880_TNRDMD_WAIT_INIT_INTVL,
+			     CXD2880_TNRDMD_WAIT_INIT_INTVL + 1000);
+	}
+
+	return cxd2880_tnrdmd_init2(tnr_dmd);
+}
+
+int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	atomic_set(&tnr_dmd->cancel, 1);
+
+	return 0;
+}
+
+int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (atomic_read(&tnr_dmd->cancel) != 0)
+		return -ECANCELED;
+
+	return 0;
+}
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..7160225db8b9
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_integ.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_INTEG_H
+#define CXD2880_INTEG_H
+
+#include "cxd2880_tnrdmd.h"
+
+#define CXD2880_TNRDMD_WAIT_INIT_TIMEOUT	500
+#define CXD2880_TNRDMD_WAIT_INIT_INTVL	10
+
+#define CXD2880_TNRDMD_WAIT_AGC_STABLE		100
+
+int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd
+				     *tnr_dmd);
+
+#endif
-- 
2.15.1

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

* [PATCH v5 07/12] [media] cxd2880: Add top level of the driver
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (5 preceding siblings ...)
  2018-01-18  8:51 ` [PATCH v5 06/12] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
@ 2018-01-18  8:52 ` Yasunari.Takiguchi
  2018-01-18  8:53 ` [PATCH v5 08/12] [media] cxd2880: Add DVB-T control functions " Yasunari.Takiguchi
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:52 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
      -changed define position of cxd2880_dvbt_t2_ops
      -modified typo about "drvier" -> "driver"
      -removed unnecessary cast
      -removed unnecessary if() 
      -modified return error code
      -removed unnecessary parentheses 
      -modified for "Lines should not end with a '(' "
      -modified to return constant 0 from read_ber function

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 | 1954 +++++++++++++++++++++
 1 file changed, 1954 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..f109e9d98cc0
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
@@ -0,0 +1,1954 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_top.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+#include <linux/spi/spi.h>
+
+#include "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 -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnrdmd);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x10);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x39, rdata, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	if ((rdata[0] & 0x01) == 0) {
+		slvt_unfreeze_reg(tnrdmd);
+		return -EAGAIN;
+	}
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x22, rdata, 2);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	*pre_bit_err = (rdata[0] << 8) | rdata[1];
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x6f, rdata, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	slvt_unfreeze_reg(tnrdmd);
+
+	*pre_bit_count = ((rdata[0] & 0x07) == 0) ?
+			 256 : (0x1000 << (rdata[0] & 0x07));
+
+	return 0;
+}
+
+static int cxd2880_pre_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+				  u32 *pre_bit_err,
+				  u32 *pre_bit_count)
+{
+	u32 period_exp = 0;
+	u32 n_ldpc = 0;
+	u8 data[5];
+	int ret;
+
+	if (!tnrdmd || !pre_bit_err || !pre_bit_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnrdmd);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x3c, data, sizeof(data));
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	if (!(data[0] & 0x01)) {
+		slvt_unfreeze_reg(tnrdmd);
+		return -EAGAIN;
+	}
+	*pre_bit_err =
+	((data[1] & 0x0f) << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0xa0, data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
+	    CXD2880_DVBT2_FEC_LDPC_16K)
+		n_ldpc = 16200;
+	else
+		n_ldpc = 64800;
+	slvt_unfreeze_reg(tnrdmd);
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x20);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x6f, data, 1);
+	if (ret)
+		return ret;
+
+	period_exp = data[0] & 0x0f;
+
+	*pre_bit_count = (1U << period_exp) * n_ldpc;
+
+	return 0;
+}
+
+static int cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
+				  u32 *post_bit_err,
+				  u32 *post_bit_count)
+{
+	u8 rdata[3];
+	u32 bit_error = 0;
+	u32 period_exp = 0;
+	int ret;
+
+	if (!tnrdmd || !post_bit_err || !post_bit_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0d);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x15, rdata, 3);
+	if (ret)
+		return ret;
+
+	if ((rdata[0] & 0x40) == 0)
+		return -EAGAIN;
+
+	*post_bit_err = ((rdata[0] & 0x3f) << 16) | (rdata[1] << 8) | rdata[2];
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x10);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x60, rdata, 1);
+	if (ret)
+		return ret;
+
+	period_exp = (rdata[0] & 0x1f);
+
+	if (period_exp <= 11 && (bit_error > (1U << period_exp) * 204 * 8))
+		return -EAGAIN;
+
+	*post_bit_count = (1U << period_exp) * 204 * 8;
+
+	return 0;
+}
+
+static int cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+				   u32 *post_bit_err,
+				   u32 *post_bit_count)
+{
+	u32 period_exp = 0;
+	u32 n_bch = 0;
+	u8 data[3];
+	enum cxd2880_dvbt2_plp_fec plp_fec_type =
+		CXD2880_DVBT2_FEC_LDPC_16K;
+	enum cxd2880_dvbt2_plp_code_rate plp_code_rate =
+		CXD2880_DVBT2_R1_2;
+	int ret;
+	static const u16 n_bch_bits_lookup[2][8] = {
+		{7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
+		{32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
+	};
+
+	if (!tnrdmd || !post_bit_err || !post_bit_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = slvt_freeze_reg(tnrdmd);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0b);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x15, data, 3);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	if (!(data[0] & 0x40)) {
+		slvt_unfreeze_reg(tnrdmd);
+		return -EAGAIN;
+	}
+
+	*post_bit_err =
+		((data[0] & 0x3f) << 16) | (data[1] << 8) | data[2];
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x9d, data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	plp_code_rate =
+	(enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0xa0, data, 1);
+	if (ret) {
+		slvt_unfreeze_reg(tnrdmd);
+		return ret;
+	}
+
+	plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
+
+	slvt_unfreeze_reg(tnrdmd);
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x20);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x72, data, 1);
+	if (ret)
+		return ret;
+
+	period_exp = data[0] & 0x0f;
+
+	if (plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K ||
+	    plp_code_rate > CXD2880_DVBT2_R2_5)
+		return -EAGAIN;
+
+	n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
+
+	if (*post_bit_err > ((1U << period_exp) * n_bch))
+		return -EAGAIN;
+
+	*post_bit_count = (1U << period_exp) * n_bch;
+
+	return 0;
+}
+
+static int cxd2880_read_block_err_t(struct cxd2880_tnrdmd *tnrdmd,
+				    u32 *block_err,
+				    u32 *block_count)
+{
+	u8 rdata[3];
+	int ret;
+
+	if (!tnrdmd || !block_err || !block_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return -EINVAL;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0d);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x18, rdata, 3);
+	if (ret)
+		return ret;
+
+	if ((rdata[0] & 0x01) == 0)
+		return -EAGAIN;
+
+	*block_err = (rdata[1] << 8) | rdata[2];
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x10);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x5c, rdata, 1);
+	if (ret)
+		return ret;
+
+	*block_count = 1U << (rdata[0] & 0x0f);
+
+	if ((*block_count == 0) || (*block_err > *block_count))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static int cxd2880_read_block_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+				     u32 *block_err,
+				     u32 *block_count)
+{
+	u8 rdata[3];
+	int ret;
+
+	if (!tnrdmd || !block_err || !block_count)
+		return -EINVAL;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return -EINVAL;
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x0b);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x18, rdata, 3);
+	if (ret)
+		return ret;
+
+	if ((rdata[0] & 0x01) == 0)
+		return -EAGAIN;
+
+	*block_err = (rdata[1] << 8) | rdata[2];
+
+	ret = tnrdmd->io->write_reg(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0x00, 0x24);
+	if (ret)
+		return ret;
+
+	ret = tnrdmd->io->read_regs(tnrdmd->io,
+				    CXD2880_IO_TGT_DMD,
+				    0xdc, rdata, 1);
+	if (ret)
+		return ret;
+
+	*block_count = 1U << (rdata[0] & 0x0f);
+
+	if ((*block_count == 0) || (*block_err > *block_count))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static void cxd2880_release(struct dvb_frontend *fe)
+{
+	struct cxd2880_priv *priv = NULL;
+
+	if (!fe) {
+		pr_err("invalid arg.\n");
+		return;
+	}
+	priv = fe->demodulator_priv;
+	kfree(priv);
+}
+
+static int cxd2880_init(struct dvb_frontend *fe)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	struct cxd2880_tnrdmd_create_param create_param;
+
+	if (!fe) {
+		pr_err("invalid arg.\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI;
+	create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE;
+	create_param.en_internal_ldo = 1;
+	create_param.xosc_cap = 18;
+	create_param.xosc_i = 8;
+	create_param.stationary_use = 1;
+
+	mutex_lock(priv->spi_mutex);
+	if (priv->tnrdmd.io != &priv->regio) {
+		ret = cxd2880_tnrdmd_create(&priv->tnrdmd,
+					    &priv->regio, &create_param);
+		if (ret) {
+			mutex_unlock(priv->spi_mutex);
+			pr_info("cxd2880 tnrdmd create failed %d\n", ret);
+			return ret;
+		}
+	}
+	ret = cxd2880_integ_init(&priv->tnrdmd);
+	if (ret) {
+		mutex_unlock(priv->spi_mutex);
+		pr_err("cxd2880 integ init failed %d\n", ret);
+		return ret;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	pr_debug("OK.\n");
+
+	return ret;
+}
+
+static int cxd2880_sleep(struct dvb_frontend *fe)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd);
+	mutex_unlock(priv->spi_mutex);
+
+	pr_debug("tnrdmd_sleep ret %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_read_signal_strength(struct dvb_frontend *fe,
+					u16 *strength)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+	int level = 0;
+
+	if (!fe || !strength) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT ||
+	    c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level);
+	} else {
+		pr_debug("invalid system\n");
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	level /= 125;
+	/*
+	 * level should be between -105dBm and -30dBm.
+	 * E.g. they should be between:
+	 * -105000/125 = -840 and -30000/125 = -240
+	 */
+	level = clamp(level, -840, -240);
+	/* scale value to 0x0000-0xffff */
+	*strength = ((level + 840) * 0xffff) / (-240 + 840);
+
+	if (ret)
+		pr_debug("ret = %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	int ret;
+	int snrvalue = 0;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if (!fe || !snr) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd,
+						  &snrvalue);
+	} else if (c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd,
+						   &snrvalue);
+	} else {
+		pr_err("invalid system\n");
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	if (snrvalue < 0)
+		snrvalue = 0;
+	*snr = snrvalue;
+
+	if (ret)
+		pr_debug("ret = %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if (!fe || !ucblocks) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(&priv->tnrdmd,
+								  ucblocks);
+	} else if (c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(&priv->tnrdmd,
+								   ucblocks);
+	} else {
+		pr_err("invalid system\n");
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	if (ret)
+		pr_debug("ret = %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	*ber = 0;
+
+	return 0;
+}
+
+static int cxd2880_set_ber_per_period_t(struct dvb_frontend *fe)
+{
+	int ret;
+	struct 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 -EINVAL;
+
+	atomic_set(&tnr_dmd->cancel, 0);
+
+	if (tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) {
+		return -ENOTTY;
+	}
+
+	ret = cxd2880_tnrdmd_dvbt_tune1(tnr_dmd, tune_param);
+	if (ret)
+		return ret;
+
+	usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
+		     CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
+
+	return cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
+}
+
+static int cxd2880_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt2_tune_param
+			      *tune_param)
+{
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	atomic_set(&tnr_dmd->cancel, 0);
+
+	if (tune_param->bandwidth != CXD2880_DTV_BW_1_7_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ &&
+	    tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) {
+		return -ENOTTY;
+	}
+
+	if (tune_param->profile != CXD2880_DVBT2_PROFILE_BASE &&
+	    tune_param->profile != CXD2880_DVBT2_PROFILE_LITE)
+		return -EINVAL;
+
+	ret = cxd2880_tnrdmd_dvbt2_tune1(tnr_dmd, tune_param);
+	if (ret)
+		return ret;
+
+	usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
+		     CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
+
+	return cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
+}
+
+static int cxd2880_set_frontend(struct dvb_frontend *fe)
+{
+	int ret;
+	struct dtv_frontend_properties *c;
+	struct cxd2880_priv *priv;
+	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->pre_bit_error.stat[0].uvalue = 0;
+	c->pre_bit_error.len = 1;
+	c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->pre_bit_count.stat[0].uvalue = 0;
+	c->pre_bit_count.len = 1;
+	c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->post_bit_error.stat[0].uvalue = 0;
+	c->post_bit_error.len = 1;
+	c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->post_bit_count.stat[0].uvalue = 0;
+	c->post_bit_count.len = 1;
+	c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->block_error.stat[0].uvalue = 0;
+	c->block_error.len = 1;
+	c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->block_count.stat[0].uvalue = 0;
+	c->block_count.len = 1;
+
+	switch (c->bandwidth_hz) {
+	case 1712000:
+		bw = CXD2880_DTV_BW_1_7_MHZ;
+		break;
+	case 5000000:
+		bw = CXD2880_DTV_BW_5_MHZ;
+		break;
+	case 6000000:
+		bw = CXD2880_DTV_BW_6_MHZ;
+		break;
+	case 7000000:
+		bw = CXD2880_DTV_BW_7_MHZ;
+		break;
+	case 8000000:
+		bw = CXD2880_DTV_BW_8_MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	priv->s = 0;
+
+	pr_info("sys:%d freq:%d bw:%d\n",
+		c->delivery_system, c->frequency, bw);
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT;
+		priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000;
+		priv->dvbt_tune_param.bandwidth = bw;
+		priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP;
+		ret = cxd2880_dvbt_tune(&priv->tnrdmd,
+					&priv->dvbt_tune_param);
+	} else if (c->delivery_system == SYS_DVBT2) {
+		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2;
+		priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000;
+		priv->dvbt2_tune_param.bandwidth = bw;
+		priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id;
+		priv->dvbt2_tune_param.profile = CXD2880_DVBT2_PROFILE_BASE;
+		ret = cxd2880_dvbt2_tune(&priv->tnrdmd,
+					 &priv->dvbt2_tune_param);
+	} else {
+		pr_err("invalid system\n");
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	pr_info("tune result %d\n", ret);
+
+	return ret;
+}
+
+static int cxd2880_get_stats(struct dvb_frontend *fe,
+			     enum fe_status status)
+{
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+	u32 pre_bit_err = 0, pre_bit_count = 0;
+	u32 post_bit_err = 0, post_bit_count = 0;
+	u32 block_err = 0, block_count = 0;
+	int ret;
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	if (!(status & FE_HAS_LOCK)) {
+		c->pre_bit_error.len = 1;
+		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->pre_bit_count.len = 1;
+		c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->post_bit_error.len = 1;
+		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->post_bit_count.len = 1;
+		c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->block_error.len = 1;
+		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->block_count.len = 1;
+		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+		return 0;
+	}
+
+	if (time_after(jiffies, priv->pre_ber_update)) {
+		priv->pre_ber_update =
+			 jiffies + msecs_to_jiffies(priv->pre_ber_interval);
+		if (c->delivery_system == SYS_DVBT) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_pre_bit_err_t(&priv->tnrdmd,
+						    &pre_bit_err,
+						    &pre_bit_count);
+			mutex_unlock(priv->spi_mutex);
+		} else if (c->delivery_system == SYS_DVBT2) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd,
+						     &pre_bit_err,
+						     &pre_bit_count);
+			mutex_unlock(priv->spi_mutex);
+		} else {
+			return -EINVAL;
+		}
+
+		if (!ret) {
+			c->pre_bit_error.len = 1;
+			c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+			c->pre_bit_error.stat[0].uvalue += pre_bit_err;
+			c->pre_bit_count.len = 1;
+			c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+			c->pre_bit_count.stat[0].uvalue += pre_bit_count;
+		} else {
+			c->pre_bit_error.len = 1;
+			c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+			c->pre_bit_count.len = 1;
+			c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+			pr_debug("pre_bit_error_t failed %d\n", ret);
+		}
+	}
+
+	if (time_after(jiffies, priv->post_ber_update)) {
+		priv->post_ber_update =
+			jiffies + msecs_to_jiffies(priv->post_ber_interval);
+		if (c->delivery_system == SYS_DVBT) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_post_bit_err_t(&priv->tnrdmd,
+						     &post_bit_err,
+						     &post_bit_count);
+			mutex_unlock(priv->spi_mutex);
+		} else if (c->delivery_system == SYS_DVBT2) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_post_bit_err_t2(&priv->tnrdmd,
+						      &post_bit_err,
+						      &post_bit_count);
+			mutex_unlock(priv->spi_mutex);
+		} else {
+			return -EINVAL;
+		}
+
+		if (!ret) {
+			c->post_bit_error.len = 1;
+			c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+			c->post_bit_error.stat[0].uvalue += post_bit_err;
+			c->post_bit_count.len = 1;
+			c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+			c->post_bit_count.stat[0].uvalue += post_bit_count;
+		} else {
+			c->post_bit_error.len = 1;
+			c->post_bit_error.stat[0].scale =
+							FE_SCALE_NOT_AVAILABLE;
+			c->post_bit_count.len = 1;
+			c->post_bit_count.stat[0].scale =
+							FE_SCALE_NOT_AVAILABLE;
+			pr_debug("post_bit_err_t %d\n", ret);
+		}
+	}
+
+	if (time_after(jiffies, priv->ucblock_update)) {
+		priv->ucblock_update =
+			jiffies + msecs_to_jiffies(priv->ucblock_interval);
+		if (c->delivery_system == SYS_DVBT) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_read_block_err_t(&priv->tnrdmd,
+						       &block_err,
+						       &block_count);
+			mutex_unlock(priv->spi_mutex);
+		} else if (c->delivery_system == SYS_DVBT2) {
+			mutex_lock(priv->spi_mutex);
+			ret = cxd2880_read_block_err_t2(&priv->tnrdmd,
+							&block_err,
+							&block_count);
+			mutex_unlock(priv->spi_mutex);
+		} else {
+			return -EINVAL;
+		}
+		if (!ret) {
+			c->block_error.len = 1;
+			c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+			c->block_error.stat[0].uvalue += block_err;
+			c->block_count.len = 1;
+			c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+			c->block_count.stat[0].uvalue += block_count;
+		} else {
+			c->block_error.len = 1;
+			c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+			c->block_count.len = 1;
+			c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+			pr_debug("read_block_err_t  %d\n", ret);
+		}
+	}
+
+	return 0;
+}
+
+static int cxd2880_check_l1post_plp(struct dvb_frontend *fe)
+{
+	u8 valid = 0;
+	u8 plp_not_found;
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+
+	if (!fe) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	ret = cxd2880_tnrdmd_dvbt2_check_l1post_valid(&priv->tnrdmd,
+						      &valid);
+	if (ret)
+		return ret;
+
+	if (!valid)
+		return -EAGAIN;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_data_plp_error(&priv->tnrdmd,
+						      &plp_not_found);
+	if (ret)
+		return ret;
+
+	if (plp_not_found) {
+		priv->dvbt2_tune_param.tune_info =
+			CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID;
+	} else {
+		priv->dvbt2_tune_param.tune_info =
+			CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK;
+	}
+
+	return 0;
+}
+
+static int cxd2880_read_status(struct dvb_frontend *fe,
+			       enum fe_status *status)
+{
+	int ret;
+	u8 sync = 0;
+	u8 lock = 0;
+	u8 unlock = 0;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if (!fe || !status) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+	*status = 0;
+
+	if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) {
+		mutex_lock(priv->spi_mutex);
+		if (c->delivery_system == SYS_DVBT) {
+			ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(&priv->tnrdmd,
+								&sync,
+								&lock,
+								&unlock);
+		} else if (c->delivery_system == SYS_DVBT2) {
+			ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(&priv->tnrdmd,
+								 &sync,
+								 &lock,
+								 &unlock);
+		} else {
+			pr_err("invalid system");
+			mutex_unlock(priv->spi_mutex);
+			return -EINVAL;
+		}
+
+		mutex_unlock(priv->spi_mutex);
+		if (ret) {
+			pr_err("failed. sys = %d\n", priv->tnrdmd.sys);
+			return  ret;
+		}
+
+		if (sync == 6) {
+			*status = FE_HAS_SIGNAL |
+				  FE_HAS_CARRIER;
+		}
+		if (lock)
+			*status |= FE_HAS_VITERBI |
+				   FE_HAS_SYNC |
+				   FE_HAS_LOCK;
+	}
+
+	pr_debug("status %d\n", *status);
+
+	if (priv->s == 0 && (*status & FE_HAS_LOCK)) {
+		mutex_lock(priv->spi_mutex);
+		if (c->delivery_system == SYS_DVBT) {
+			ret = cxd2880_set_ber_per_period_t(fe);
+			priv->s = *status;
+		} else if (c->delivery_system == SYS_DVBT2) {
+			ret = cxd2880_check_l1post_plp(fe);
+			if (!ret) {
+				ret = cxd2880_set_ber_per_period_t2(fe);
+				priv->s = *status;
+			}
+		} else {
+			pr_err("invalid system\n");
+			mutex_unlock(priv->spi_mutex);
+			return -EINVAL;
+		}
+		mutex_unlock(priv->spi_mutex);
+	}
+
+	cxd2880_get_stats(fe, *status);
+	return  0;
+}
+
+static int cxd2880_tune(struct dvb_frontend *fe,
+			bool retune,
+			unsigned int mode_flags,
+			unsigned int *delay,
+			enum fe_status *status)
+{
+	int ret;
+
+	if (!fe || !delay || !status) {
+		pr_err("invalid arg.");
+		return -EINVAL;
+	}
+
+	if (retune) {
+		ret = cxd2880_set_frontend(fe);
+		if (ret) {
+			pr_err("cxd2880_set_frontend failed %d\n", ret);
+			return ret;
+		}
+	}
+
+	*delay = HZ / 5;
+
+	return cxd2880_read_status(fe, status);
+}
+
+static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
+				  struct dtv_frontend_properties *c)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
+	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
+	struct cxd2880_dvbt_tpsinfo tps;
+	enum cxd2880_tnrdmd_spectrum_sense sense;
+	u16 snr = 0;
+	int strength = 0;
+
+	if (!fe || !c) {
+		pr_err("invalid arg\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
+						 &mode, &guard);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (mode) {
+		case CXD2880_DVBT_MODE_2K:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			break;
+		case CXD2880_DVBT_MODE_8K:
+			c->transmission_mode = TRANSMISSION_MODE_8K;
+			break;
+		default:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			pr_debug("transmission mode is invalid %d\n", mode);
+			break;
+		}
+		switch (guard) {
+		case CXD2880_DVBT_GUARD_1_32:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case CXD2880_DVBT_GUARD_1_16:
+			c->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case CXD2880_DVBT_GUARD_1_8:
+			c->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case CXD2880_DVBT_GUARD_1_4:
+			c->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		default:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			pr_debug("guard interval is invalid %d\n",
+				 guard);
+			break;
+		}
+	} else {
+		c->transmission_mode = TRANSMISSION_MODE_2K;
+		c->guard_interval = GUARD_INTERVAL_1_32;
+		pr_debug("ModeGuard err %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (tps.hierarchy) {
+		case CXD2880_DVBT_HIERARCHY_NON:
+			c->hierarchy = HIERARCHY_NONE;
+			break;
+		case CXD2880_DVBT_HIERARCHY_1:
+			c->hierarchy = HIERARCHY_1;
+			break;
+		case CXD2880_DVBT_HIERARCHY_2:
+			c->hierarchy = HIERARCHY_2;
+			break;
+		case CXD2880_DVBT_HIERARCHY_4:
+			c->hierarchy = HIERARCHY_4;
+			break;
+		default:
+			c->hierarchy = HIERARCHY_NONE;
+			pr_debug("TPSInfo hierarchy is invalid %d\n",
+				 tps.hierarchy);
+			break;
+		}
+
+		switch (tps.rate_hp) {
+		case CXD2880_DVBT_CODERATE_1_2:
+			c->code_rate_HP = FEC_1_2;
+			break;
+		case CXD2880_DVBT_CODERATE_2_3:
+			c->code_rate_HP = FEC_2_3;
+			break;
+		case CXD2880_DVBT_CODERATE_3_4:
+			c->code_rate_HP = FEC_3_4;
+			break;
+		case CXD2880_DVBT_CODERATE_5_6:
+			c->code_rate_HP = FEC_5_6;
+			break;
+		case CXD2880_DVBT_CODERATE_7_8:
+			c->code_rate_HP = FEC_7_8;
+			break;
+		default:
+			c->code_rate_HP = FEC_NONE;
+			pr_debug("TPSInfo rateHP is invalid %d\n",
+				 tps.rate_hp);
+			break;
+		}
+		switch (tps.rate_lp) {
+		case CXD2880_DVBT_CODERATE_1_2:
+			c->code_rate_LP = FEC_1_2;
+			break;
+		case CXD2880_DVBT_CODERATE_2_3:
+			c->code_rate_LP = FEC_2_3;
+			break;
+		case CXD2880_DVBT_CODERATE_3_4:
+			c->code_rate_LP = FEC_3_4;
+			break;
+		case CXD2880_DVBT_CODERATE_5_6:
+			c->code_rate_LP = FEC_5_6;
+			break;
+		case CXD2880_DVBT_CODERATE_7_8:
+			c->code_rate_LP = FEC_7_8;
+			break;
+		default:
+			c->code_rate_LP = FEC_NONE;
+			pr_debug("TPSInfo rateLP is invalid %d\n",
+				 tps.rate_lp);
+			break;
+		}
+		switch (tps.constellation) {
+		case CXD2880_DVBT_CONSTELLATION_QPSK:
+			c->modulation = QPSK;
+			break;
+		case CXD2880_DVBT_CONSTELLATION_16QAM:
+			c->modulation = QAM_16;
+			break;
+		case CXD2880_DVBT_CONSTELLATION_64QAM:
+			c->modulation = QAM_64;
+			break;
+		default:
+			c->modulation = QPSK;
+			pr_debug("TPSInfo constellation is invalid %d\n",
+				 tps.constellation);
+			break;
+		}
+	} else {
+		c->hierarchy = HIERARCHY_NONE;
+		c->code_rate_HP = FEC_NONE;
+		c->code_rate_LP = FEC_NONE;
+		c->modulation = QPSK;
+		pr_debug("TPS info err %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (sense) {
+		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+			c->inversion = INVERSION_OFF;
+			break;
+		case CXD2880_TNRDMD_SPECTRUM_INV:
+			c->inversion = INVERSION_ON;
+			break;
+		default:
+			c->inversion = INVERSION_OFF;
+			pr_debug("spectrum sense is invalid %d\n", sense);
+			break;
+		}
+	} else {
+		c->inversion = INVERSION_OFF;
+		pr_debug("spectrum_sense %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+		c->strength.stat[0].svalue = strength;
+	} else {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		pr_debug("mon_rf_lvl %d\n", ret);
+	}
+
+	ret = cxd2880_read_snr(fe, &snr);
+	if (!ret) {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = snr;
+	} else {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		pr_debug("read_snr %d\n", ret);
+	}
+
+	return 0;
+}
+
+static int cxd2880_get_frontend_t2(struct dvb_frontend *fe,
+				   struct dtv_frontend_properties *c)
+{
+	int ret;
+	struct cxd2880_priv *priv = NULL;
+	struct cxd2880_dvbt2_l1pre l1pre;
+	enum cxd2880_dvbt2_plp_code_rate coderate;
+	enum cxd2880_dvbt2_plp_constell qam;
+	enum cxd2880_tnrdmd_spectrum_sense sense;
+	u16 snr = 0;
+	int strength = 0;
+
+	if (!fe || !c) {
+		pr_err("invalid arg.\n");
+		return -EINVAL;
+	}
+
+	priv = fe->demodulator_priv;
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (l1pre.fft_mode) {
+		case CXD2880_DVBT2_M2K:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			break;
+		case CXD2880_DVBT2_M8K:
+			c->transmission_mode = TRANSMISSION_MODE_8K;
+			break;
+		case CXD2880_DVBT2_M4K:
+			c->transmission_mode = TRANSMISSION_MODE_4K;
+			break;
+		case CXD2880_DVBT2_M1K:
+			c->transmission_mode = TRANSMISSION_MODE_1K;
+			break;
+		case CXD2880_DVBT2_M16K:
+			c->transmission_mode = TRANSMISSION_MODE_16K;
+			break;
+		case CXD2880_DVBT2_M32K:
+			c->transmission_mode = TRANSMISSION_MODE_32K;
+			break;
+		default:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			pr_debug("L1Pre fft_mode is invalid %d\n",
+				 l1pre.fft_mode);
+			break;
+		}
+		switch (l1pre.gi) {
+		case CXD2880_DVBT2_G1_32:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case CXD2880_DVBT2_G1_16:
+			c->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case CXD2880_DVBT2_G1_8:
+			c->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case CXD2880_DVBT2_G1_4:
+			c->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		case CXD2880_DVBT2_G1_128:
+			c->guard_interval = GUARD_INTERVAL_1_128;
+			break;
+		case CXD2880_DVBT2_G19_128:
+			c->guard_interval = GUARD_INTERVAL_19_128;
+			break;
+		case CXD2880_DVBT2_G19_256:
+			c->guard_interval = GUARD_INTERVAL_19_256;
+			break;
+		default:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			pr_debug("L1Pre guard interval is invalid %d\n",
+				 l1pre.gi);
+			break;
+		}
+	} else {
+		c->transmission_mode = TRANSMISSION_MODE_2K;
+		c->guard_interval = GUARD_INTERVAL_1_32;
+		pr_debug("L1Pre err %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd,
+						 CXD2880_DVBT2_PLP_DATA,
+						 &coderate);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (coderate) {
+		case CXD2880_DVBT2_R1_2:
+			c->fec_inner = FEC_1_2;
+			break;
+		case CXD2880_DVBT2_R3_5:
+			c->fec_inner = FEC_3_5;
+			break;
+		case CXD2880_DVBT2_R2_3:
+			c->fec_inner = FEC_2_3;
+			break;
+		case CXD2880_DVBT2_R3_4:
+			c->fec_inner = FEC_3_4;
+			break;
+		case CXD2880_DVBT2_R4_5:
+			c->fec_inner = FEC_4_5;
+			break;
+		case CXD2880_DVBT2_R5_6:
+			c->fec_inner = FEC_5_6;
+			break;
+		default:
+			c->fec_inner = FEC_NONE;
+			pr_debug("CodeRate is invalid %d\n", coderate);
+			break;
+		}
+	} else {
+		c->fec_inner = FEC_NONE;
+		pr_debug("CodeRate %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd,
+					   CXD2880_DVBT2_PLP_DATA,
+					   &qam);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (qam) {
+		case CXD2880_DVBT2_QPSK:
+			c->modulation = QPSK;
+			break;
+		case CXD2880_DVBT2_QAM16:
+			c->modulation = QAM_16;
+			break;
+		case CXD2880_DVBT2_QAM64:
+			c->modulation = QAM_64;
+			break;
+		case CXD2880_DVBT2_QAM256:
+			c->modulation = QAM_256;
+			break;
+		default:
+			c->modulation = QPSK;
+			pr_debug("QAM is invalid %d\n", qam);
+			break;
+		}
+	} else {
+		c->modulation = QPSK;
+		pr_debug("QAM %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		switch (sense) {
+		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+			c->inversion = INVERSION_OFF;
+			break;
+		case CXD2880_TNRDMD_SPECTRUM_INV:
+			c->inversion = INVERSION_ON;
+			break;
+		default:
+			c->inversion = INVERSION_OFF;
+			pr_debug("spectrum sense is invalid %d\n", sense);
+			break;
+		}
+	} else {
+		c->inversion = INVERSION_OFF;
+		pr_debug("SpectrumSense %d\n", ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+	mutex_unlock(priv->spi_mutex);
+	if (!ret) {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+		c->strength.stat[0].svalue = strength;
+	} else {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		pr_debug("mon_rf_lvl %d\n", ret);
+	}
+
+	ret = cxd2880_read_snr(fe, &snr);
+	if (!ret) {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = snr;
+	} else {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		pr_debug("read_snr %d\n", ret);
+	}
+
+	return 0;
+}
+
+static int cxd2880_get_frontend(struct dvb_frontend *fe,
+				struct dtv_frontend_properties *props)
+{
+	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 = {
+	.info = {
+		.name = "Sony CXD2880",
+		.frequency_min =  174000000,
+		.frequency_max = 862000000,
+		.frequency_stepsize = 1000,
+		.caps = FE_CAN_INVERSION_AUTO |
+				FE_CAN_FEC_1_2 |
+				FE_CAN_FEC_2_3 |
+				FE_CAN_FEC_3_4 |
+				FE_CAN_FEC_4_5 |
+				FE_CAN_FEC_5_6	|
+				FE_CAN_FEC_7_8	|
+				FE_CAN_FEC_AUTO |
+				FE_CAN_QPSK |
+				FE_CAN_QAM_16 |
+				FE_CAN_QAM_32 |
+				FE_CAN_QAM_64 |
+				FE_CAN_QAM_128 |
+				FE_CAN_QAM_256 |
+				FE_CAN_QAM_AUTO |
+				FE_CAN_TRANSMISSION_MODE_AUTO |
+				FE_CAN_GUARD_INTERVAL_AUTO |
+				FE_CAN_2G_MODULATION |
+				FE_CAN_RECOVER |
+				FE_CAN_MUTE_TS,
+	},
+	.delsys = { SYS_DVBT, SYS_DVBT2 },
+
+	.release = cxd2880_release,
+	.init = cxd2880_init,
+	.sleep = cxd2880_sleep,
+	.tune = cxd2880_tune,
+	.set_frontend = cxd2880_set_frontend,
+	.get_frontend = cxd2880_get_frontend,
+	.read_status = cxd2880_read_status,
+	.read_ber = cxd2880_read_ber,
+	.read_signal_strength = cxd2880_read_signal_strength,
+	.read_snr = cxd2880_read_snr,
+	.read_ucblocks = cxd2880_read_ucblocks,
+	.get_frontend_algo = cxd2880_get_frontend_algo,
+};
+
+struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+				    struct cxd2880_config *cfg)
+{
+	int ret;
+	enum cxd2880_tnrdmd_chip_id chipid =
+					CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+	static struct cxd2880_priv *priv;
+	u8 data = 0;
+
+	if (!fe) {
+		pr_err("invalid arg.\n");
+		return NULL;
+	}
+
+	priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->spi = cfg->spi;
+	priv->spi_mutex = cfg->spi_mutex;
+	priv->spi_device.spi = cfg->spi;
+
+	memcpy(&fe->ops, &cxd2880_dvbt_t2_ops,
+	       sizeof(struct dvb_frontend_ops));
+
+	ret = cxd2880_spi_device_initialize(&priv->spi_device,
+					    CXD2880_SPI_MODE_0,
+					    55000000);
+	if (ret) {
+		pr_err("spi_device_initialize failed. %d\n", ret);
+		kfree(priv);
+		return NULL;
+	}
+
+	ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi,
+					    &priv->spi_device);
+	if (ret) {
+		pr_err("spi_device_create_spi failed. %d\n", ret);
+		kfree(priv);
+		return NULL;
+	}
+
+	ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0);
+	if (ret) {
+		pr_err("io_spi_create failed. %d\n", ret);
+		kfree(priv);
+		return NULL;
+	}
+	ret = priv->regio.write_reg(&priv->regio,
+				    CXD2880_IO_TGT_SYS, 0x00, 0x00);
+	if (ret) {
+		pr_err("set bank to 0x00 failed.\n");
+		kfree(priv);
+		return NULL;
+	}
+	ret = priv->regio.read_regs(&priv->regio,
+				    CXD2880_IO_TGT_SYS, 0xfd, &data, 1);
+	if (ret) {
+		pr_err("read chip id failed.\n");
+		kfree(priv);
+		return NULL;
+	}
+
+	chipid = (enum cxd2880_tnrdmd_chip_id)data;
+	if (chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X &&
+	    chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11) {
+		pr_err("chip id invalid.\n");
+		kfree(priv);
+		return NULL;
+	}
+
+	fe->demodulator_priv = priv;
+	pr_info("CXD2880 driver version: Ver %s\n",
+		CXD2880_TNRDMD_DRIVER_VERSION);
+
+	return fe;
+}
+EXPORT_SYMBOL(cxd2880_attach);
+
+MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
-- 
2.15.1

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

* [PATCH v5 08/12] [media] cxd2880: Add DVB-T control functions the driver
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (6 preceding siblings ...)
  2018-01-18  8:52 ` [PATCH v5 07/12] [media] cxd2880: Add top level of " Yasunari.Takiguchi
@ 2018-01-18  8:53 ` Yasunari.Takiguchi
  2018-01-18  8:54 ` [PATCH v5 09/12] [media] cxd2880: Add DVB-T monitor functions Yasunari.Takiguchi
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:53 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
      -removed unnecessary if()        
      -modified return error code
      -removed unnecessary parentheses 

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 |  74 ++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c    | 919 +++++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h    |  45 +
 3 files changed, 1038 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..76a1acc346ef
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T related definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DVBT_H
+#define CXD2880_DVBT_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt_constellation {
+	CXD2880_DVBT_CONSTELLATION_QPSK,
+	CXD2880_DVBT_CONSTELLATION_16QAM,
+	CXD2880_DVBT_CONSTELLATION_64QAM,
+	CXD2880_DVBT_CONSTELLATION_RESERVED_3
+};
+
+enum cxd2880_dvbt_hierarchy {
+	CXD2880_DVBT_HIERARCHY_NON,
+	CXD2880_DVBT_HIERARCHY_1,
+	CXD2880_DVBT_HIERARCHY_2,
+	CXD2880_DVBT_HIERARCHY_4
+};
+
+enum cxd2880_dvbt_coderate {
+	CXD2880_DVBT_CODERATE_1_2,
+	CXD2880_DVBT_CODERATE_2_3,
+	CXD2880_DVBT_CODERATE_3_4,
+	CXD2880_DVBT_CODERATE_5_6,
+	CXD2880_DVBT_CODERATE_7_8,
+	CXD2880_DVBT_CODERATE_RESERVED_5,
+	CXD2880_DVBT_CODERATE_RESERVED_6,
+	CXD2880_DVBT_CODERATE_RESERVED_7
+};
+
+enum cxd2880_dvbt_guard {
+	CXD2880_DVBT_GUARD_1_32,
+	CXD2880_DVBT_GUARD_1_16,
+	CXD2880_DVBT_GUARD_1_8,
+	CXD2880_DVBT_GUARD_1_4
+};
+
+enum cxd2880_dvbt_mode {
+	CXD2880_DVBT_MODE_2K,
+	CXD2880_DVBT_MODE_8K,
+	CXD2880_DVBT_MODE_RESERVED_2,
+	CXD2880_DVBT_MODE_RESERVED_3
+};
+
+enum cxd2880_dvbt_profile {
+	CXD2880_DVBT_PROFILE_HP = 0,
+	CXD2880_DVBT_PROFILE_LP
+};
+
+struct cxd2880_dvbt_tpsinfo {
+	enum cxd2880_dvbt_constellation constellation;
+	enum cxd2880_dvbt_hierarchy hierarchy;
+	enum cxd2880_dvbt_coderate rate_hp;
+	enum cxd2880_dvbt_coderate rate_lp;
+	enum cxd2880_dvbt_guard guard;
+	enum cxd2880_dvbt_mode mode;
+	u8 fnum;
+	u8 length_indicator;
+	u16 cell_id;
+	u8 cell_id_ok;
+	u8 reserved_even;
+	u8 reserved_odd;
+};
+
+#endif
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..e1ad5187ad8f
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
@@ -0,0 +1,919 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "dvb_frontend.h"
+
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = {
+	{0x00, 0x00}, {0x31, 0x01},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = {
+	{0x00, 0x04}, {0x5c, 0xfb}, {0x00, 0x10}, {0xa4, 0x03},
+	{0x00, 0x14}, {0xb0, 0x00}, {0x00, 0x25},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq3[] = {
+	{0x00, 0x12}, {0x44, 0x00},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq4[] = {
+	{0x00, 0x11}, {0x87, 0xd2},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq5[] = {
+	{0x00, 0x00}, {0xfd, 0x01},
+};
+
+static const struct cxd2880_reg_value sleep_dmd_setting_seq1[] = {
+	{0x00, 0x04}, {0x5c, 0xd8}, {0x00, 0x10}, {0xa4, 0x00},
+};
+
+static const struct cxd2880_reg_value sleep_dmd_setting_seq2[] = {
+	{0x00, 0x11}, {0x87, 0x04},
+};
+
+static int x_tune_dvbt_demod_setting(struct cxd2880_tnrdmd
+				     *tnr_dmd,
+				     enum cxd2880_dtv_bandwidth
+				     bandwidth,
+				     enum cxd2880_tnrdmd_clockmode
+				     clk_mode)
+{
+	static const u8 clk_mode_ckffrq_a[2] = { 0x52, 0x49 };
+	static const u8 clk_mode_ckffrq_b[2] = { 0x5d, 0x55 };
+	static const u8 clk_mode_ckffrq_c[2] = { 0x60, 0x00 };
+	static const u8 ratectl_margin[2] = { 0x01, 0xf0 };
+	static const u8 maxclkcnt_a[3] = { 0x73, 0xca, 0x49 };
+	static const u8 maxclkcnt_b[3] = { 0xc8, 0x13, 0xaa };
+	static const u8 maxclkcnt_c[3] = { 0xdc, 0x6c, 0x00 };
+
+	static const u8 bw8_nomi_ac[5] = { 0x15, 0x00, 0x00, 0x00, 0x00};
+	static const u8 bw8_nomi_b[5] = { 0x14, 0x6a, 0xaa, 0xaa, 0xaa};
+	static const u8 bw8_gtdofst_a[2] = { 0x01, 0x28 };
+	static const u8 bw8_gtdofst_b[2] = { 0x11, 0x44 };
+	static const u8 bw8_gtdofst_c[2] = { 0x15, 0x28 };
+	static const u8 bw8_mrc_a[5] = { 0x30, 0x00, 0x00, 0x90, 0x00 };
+	static const u8 bw8_mrc_b[5] = { 0x36, 0x71, 0x00, 0xa3, 0x55 };
+	static const u8 bw8_mrc_c[5] = { 0x38, 0x00, 0x00, 0xa8, 0x00 };
+	static const u8 bw8_notch[4] = { 0xb3, 0x00, 0x01, 0x02 };
+
+	static const u8 bw7_nomi_ac[5] = { 0x18, 0x00, 0x00, 0x00, 0x00};
+	static const u8 bw7_nomi_b[5] = { 0x17, 0x55, 0x55, 0x55, 0x55};
+	static const u8 bw7_gtdofst_a[2] = { 0x12, 0x4c };
+	static const u8 bw7_gtdofst_b[2] = { 0x1f, 0x15 };
+	static const u8 bw7_gtdofst_c[2] = { 0x1f, 0xf8 };
+	static const u8 bw7_mrc_a[5] = { 0x36, 0xdb, 0x00, 0xa4, 0x92 };
+	static const u8 bw7_mrc_b[5] = { 0x3e, 0x38, 0x00, 0xba, 0xaa };
+	static const u8 bw7_mrc_c[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 };
+	static const u8 bw7_notch[4] = { 0xb8, 0x00, 0x00, 0x03 };
+
+	static const u8 bw6_nomi_ac[5] = { 0x1c, 0x00, 0x00, 0x00, 0x00};
+	static const u8 bw6_nomi_b[5] = { 0x1b, 0x38, 0xe3, 0x8e, 0x38};
+	static const u8 bw6_gtdofst_a[2] = { 0x1f, 0xf8 };
+	static const u8 bw6_gtdofst_b[2] = { 0x24, 0x43 };
+	static const u8 bw6_gtdofst_c[2] = { 0x25, 0x4c };
+	static const u8 bw6_mrc_a[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 };
+	static const u8 bw6_mrc_b[5] = { 0x48, 0x97, 0x00, 0xd9, 0xc7 };
+	static const u8 bw6_mrc_c[5] = { 0x4a, 0xaa, 0x00, 0xdf, 0xff };
+	static const u8 bw6_notch[4] = { 0xbe, 0xab, 0x00, 0x03 };
+
+	static const u8 bw5_nomi_ac[5] = { 0x21, 0x99, 0x99, 0x99, 0x99};
+	static const u8 bw5_nomi_b[5] = { 0x20, 0xaa, 0xaa, 0xaa, 0xaa};
+	static const u8 bw5_gtdofst_a[2] = { 0x26, 0x5d };
+	static const u8 bw5_gtdofst_b[2] = { 0x2b, 0x84 };
+	static const u8 bw5_gtdofst_c[2] = { 0x2c, 0xc2 };
+	static const u8 bw5_mrc_a[5] = { 0x4c, 0xcc, 0x00, 0xe6, 0x66 };
+	static const u8 bw5_mrc_b[5] = { 0x57, 0x1c, 0x01, 0x05, 0x55 };
+	static const u8 bw5_mrc_c[5] = { 0x59, 0x99, 0x01, 0x0c, 0xcc };
+	static const u8 bw5_notch[4] = { 0xc8, 0x01, 0x00, 0x03 };
+	const u8 *data = NULL;
+	u8 sst_data;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  tune_dmd_setting_seq1,
+					  ARRAY_SIZE(tune_dmd_setting_seq1));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret)
+		return ret;
+
+	switch (clk_mode) {
+	case CXD2880_TNRDMD_CLOCKMODE_A:
+		data = clk_mode_ckffrq_a;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_B:
+		data = clk_mode_ckffrq_b;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_C:
+		data = clk_mode_ckffrq_c;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x65, data, 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x5d, 0x07);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		u8 data[2] = { 0x01, 0x01 };
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x00);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0xce, data, 2);
+		if (ret)
+			return ret;
+	}
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  tune_dmd_setting_seq2,
+					  ARRAY_SIZE(tune_dmd_setting_seq2));
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xf0, ratectl_margin, 2);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN ||
+	    tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+						  CXD2880_IO_TGT_DMD,
+						  tune_dmd_setting_seq3,
+						  ARRAY_SIZE(tune_dmd_setting_seq3));
+		if (ret)
+			return ret;
+	}
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+						  CXD2880_IO_TGT_DMD,
+						  tune_dmd_setting_seq4,
+						  ARRAY_SIZE(tune_dmd_setting_seq4));
+		if (ret)
+			return ret;
+	}
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x04);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = maxclkcnt_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = maxclkcnt_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = maxclkcnt_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x68, data, 3);
+		if (ret)
+			return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret)
+		return ret;
+
+	switch (bandwidth) {
+	case CXD2880_DTV_BW_8_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw8_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw8_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x60, data, 5);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x00);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw8_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw8_gtdofst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw8_gtdofst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x7d, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			sst_data = 0x35;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			sst_data = 0x34;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x71, sst_data);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw8_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw8_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw8_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, &data[0], 2);
+			if (ret)
+				return ret;
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x51, &data[2], 3);
+			if (ret)
+				return ret;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x72, &bw8_notch[0], 2);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x6b, &bw8_notch[2], 2);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_DTV_BW_7_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw7_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw7_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x60, data, 5);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x02);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw7_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw7_gtdofst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw7_gtdofst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x7d, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			sst_data = 0x2f;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			sst_data = 0x2e;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x71, sst_data);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw7_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw7_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw7_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, &data[0], 2);
+			if (ret)
+				return ret;
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x51, &data[2], 3);
+			if (ret)
+				return ret;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x72, &bw7_notch[0], 2);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x6b, &bw7_notch[2], 2);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_DTV_BW_6_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw6_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw6_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x60, data, 5);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x04);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw6_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw6_gtdofst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw6_gtdofst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x7d, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			sst_data = 0x29;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			sst_data = 0x2a;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x71, sst_data);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw6_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw6_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw6_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, &data[0], 2);
+			if (ret)
+				return ret;
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x51, &data[2], 3);
+			if (ret)
+				return ret;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x72, &bw6_notch[0], 2);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x6b, &bw6_notch[2], 2);
+		if (ret)
+			return ret;
+		break;
+
+	case CXD2880_DTV_BW_5_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw5_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw5_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x60, data, 5);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x06);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw5_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw5_gtdofst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw5_gtdofst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x7d, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			sst_data = 0x24;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			sst_data = 0x23;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x71, sst_data);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw5_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw5_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw5_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, &data[0], 2);
+			if (ret)
+				return ret;
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x51, &data[2], 3);
+			if (ret)
+				return ret;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x72, &bw5_notch[0], 2);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x6b, &bw5_notch[2], 2);
+		if (ret)
+			return ret;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return cxd2880_io_write_multi_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD,
+					   tune_dmd_setting_seq5,
+					   ARRAY_SIZE(tune_dmd_setting_seq5));
+}
+
+static int x_sleep_dvbt_demod_setting(struct cxd2880_tnrdmd
+						   *tnr_dmd)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  sleep_dmd_setting_seq1,
+					  ARRAY_SIZE(sleep_dmd_setting_seq1));
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+						  CXD2880_IO_TGT_DMD,
+						  sleep_dmd_setting_seq2,
+						  ARRAY_SIZE(sleep_dmd_setting_seq2));
+
+	return ret;
+}
+
+static int dvbt_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+			    enum cxd2880_dvbt_profile profile)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x10);
+	if (ret)
+		return ret;
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x67,
+				      (profile == CXD2880_DVBT_PROFILE_HP)
+				      ? 0x00 : 0x01);
+}
+
+int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt_tune_param
+			      *tune_param)
+{
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+						tune_param->center_freq_khz,
+						tune_param->bandwidth, 0, 0);
+	if (ret)
+		return ret;
+
+	ret =
+	    x_tune_dvbt_demod_setting(tnr_dmd, tune_param->bandwidth,
+				      tnr_dmd->clk_mode);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    x_tune_dvbt_demod_setting(tnr_dmd->diver_sub,
+					      tune_param->bandwidth,
+					      tnr_dmd->diver_sub->clk_mode);
+		if (ret)
+			return ret;
+	}
+
+	return dvbt_set_profile(tnr_dmd, tune_param->profile);
+}
+
+int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt_tune_param
+			      *tune_param)
+{
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+						0);
+	if (ret)
+		return ret;
+
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+	tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+	tnr_dmd->sys = CXD2880_DTV_SYS_DVBT;
+	tnr_dmd->bandwidth = tune_param->bandwidth;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+		tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT;
+		tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = x_sleep_dvbt_demod_setting(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+		ret = x_sleep_dvbt_demod_setting(tnr_dmd->diver_sub);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+					 *tnr_dmd,
+					 enum
+					 cxd2880_tnrdmd_lock_result
+					 *lock)
+{
+	int ret;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if (!tnr_dmd || !lock)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					      &unlock_detected);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (sync_stat == 6)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (sync_stat == 6) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						  &unlock_detected_sub);
+	if (ret)
+		return ret;
+
+	if (sync_stat == 6)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+	else if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+				      *tnr_dmd,
+				      enum
+				      cxd2880_tnrdmd_lock_result
+				      *lock)
+{
+	int ret;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if (!tnr_dmd || !lock)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					      &unlock_detected);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (ts_lock)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (ts_lock) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	} else if (!unlock_detected) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						  &unlock_detected_sub);
+	if (ret)
+		return ret;
+
+	if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
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..35d81ccc732b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT_H
+#define CXD2880_TNRDMD_DVBT_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+struct cxd2880_dvbt_tune_param {
+	u32 center_freq_khz;
+	enum cxd2880_dtv_bandwidth bandwidth;
+	enum cxd2880_dvbt_profile profile;
+};
+
+int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt_tune_param
+			      *tune_param);
+
+int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+			      struct cxd2880_dvbt_tune_param
+			      *tune_param);
+
+int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd
+				      *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+					 *tnr_dmd,
+					 enum
+					 cxd2880_tnrdmd_lock_result
+					 *lock);
+
+int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+				      *tnr_dmd,
+				      enum
+				      cxd2880_tnrdmd_lock_result
+				      *lock);
+
+#endif
-- 
2.15.1

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

* [PATCH v5 09/12] [media] cxd2880: Add DVB-T monitor functions
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (7 preceding siblings ...)
  2018-01-18  8:53 ` [PATCH v5 08/12] [media] cxd2880: Add DVB-T control functions " Yasunari.Takiguchi
@ 2018-01-18  8:54 ` Yasunari.Takiguchi
  2018-01-18  8:55 ` [PATCH v5 10/12] [media] cxd2880: Add DVB-T2 control functions for the driver Yasunari.Takiguchi
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:54 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
      -removed unnecessary if()        
      -modified return error code
      -removed unnecessary parentheses 
      -modified for "Lines should not end with a '(' "
      -removed unnecessary functions
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
      -removed unnecessary functions

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

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

* [PATCH v5 10/12] [media] cxd2880: Add DVB-T2 control functions for the driver
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (8 preceding siblings ...)
  2018-01-18  8:54 ` [PATCH v5 09/12] [media] cxd2880: Add DVB-T monitor functions Yasunari.Takiguchi
@ 2018-01-18  8:55 ` Yasunari.Takiguchi
  2018-01-18  8:57 ` [PATCH v5 11/12] [media] cxd2880: Add DVB-T2 monitor functions Yasunari.Takiguchi
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:55 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
      -removed unnecessary if()        
      -modified return error code
      -removed unnecessary parentheses 

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    |  385 +++++++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c   | 1217 ++++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h   |   65 ++
 3 files changed, 1667 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..191047b158fe
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
@@ -0,0 +1,385 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 related definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DVBT2_H
+#define CXD2880_DVBT2_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt2_profile {
+	CXD2880_DVBT2_PROFILE_BASE,
+	CXD2880_DVBT2_PROFILE_LITE,
+	CXD2880_DVBT2_PROFILE_ANY
+};
+
+enum cxd2880_dvbt2_version {
+	CXD2880_DVBT2_V111,
+	CXD2880_DVBT2_V121,
+	CXD2880_DVBT2_V131
+};
+
+enum cxd2880_dvbt2_s1 {
+	CXD2880_DVBT2_S1_BASE_SISO = 0x00,
+	CXD2880_DVBT2_S1_BASE_MISO = 0x01,
+	CXD2880_DVBT2_S1_NON_DVBT2 = 0x02,
+	CXD2880_DVBT2_S1_LITE_SISO = 0x03,
+	CXD2880_DVBT2_S1_LITE_MISO = 0x04,
+	CXD2880_DVBT2_S1_RSVD3 = 0x05,
+	CXD2880_DVBT2_S1_RSVD4 = 0x06,
+	CXD2880_DVBT2_S1_RSVD5 = 0x07,
+	CXD2880_DVBT2_S1_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_base_s2 {
+	CXD2880_DVBT2_BASE_S2_M2K_G_ANY = 0x00,
+	CXD2880_DVBT2_BASE_S2_M8K_G_DVBT = 0x01,
+	CXD2880_DVBT2_BASE_S2_M4K_G_ANY = 0x02,
+	CXD2880_DVBT2_BASE_S2_M1K_G_ANY = 0x03,
+	CXD2880_DVBT2_BASE_S2_M16K_G_ANY = 0x04,
+	CXD2880_DVBT2_BASE_S2_M32K_G_DVBT = 0x05,
+	CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2 = 0x06,
+	CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2 = 0x07,
+	CXD2880_DVBT2_BASE_S2_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_lite_s2 {
+	CXD2880_DVBT2_LITE_S2_M2K_G_ANY = 0x00,
+	CXD2880_DVBT2_LITE_S2_M8K_G_DVBT = 0x01,
+	CXD2880_DVBT2_LITE_S2_M4K_G_ANY = 0x02,
+	CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2 = 0x03,
+	CXD2880_DVBT2_LITE_S2_M16K_G_DVBT = 0x04,
+	CXD2880_DVBT2_LITE_S2_RSVD1 = 0x05,
+	CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2 = 0x06,
+	CXD2880_DVBT2_LITE_S2_RSVD2 = 0x07,
+	CXD2880_DVBT2_LITE_S2_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_guard {
+	CXD2880_DVBT2_G1_32 = 0x00,
+	CXD2880_DVBT2_G1_16 = 0x01,
+	CXD2880_DVBT2_G1_8 = 0x02,
+	CXD2880_DVBT2_G1_4 = 0x03,
+	CXD2880_DVBT2_G1_128 = 0x04,
+	CXD2880_DVBT2_G19_128 = 0x05,
+	CXD2880_DVBT2_G19_256 = 0x06,
+	CXD2880_DVBT2_G_RSVD1 = 0x07,
+	CXD2880_DVBT2_G_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_mode {
+	CXD2880_DVBT2_M2K = 0x00,
+	CXD2880_DVBT2_M8K = 0x01,
+	CXD2880_DVBT2_M4K = 0x02,
+	CXD2880_DVBT2_M1K = 0x03,
+	CXD2880_DVBT2_M16K = 0x04,
+	CXD2880_DVBT2_M32K = 0x05,
+	CXD2880_DVBT2_M_RSVD1 = 0x06,
+	CXD2880_DVBT2_M_RSVD2 = 0x07
+};
+
+enum cxd2880_dvbt2_bw {
+	CXD2880_DVBT2_BW_8 = 0x00,
+	CXD2880_DVBT2_BW_7 = 0x01,
+	CXD2880_DVBT2_BW_6 = 0x02,
+	CXD2880_DVBT2_BW_5 = 0x03,
+	CXD2880_DVBT2_BW_10 = 0x04,
+	CXD2880_DVBT2_BW_1_7 = 0x05,
+	CXD2880_DVBT2_BW_RSVD1 = 0x06,
+	CXD2880_DVBT2_BW_RSVD2 = 0x07,
+	CXD2880_DVBT2_BW_RSVD3 = 0x08,
+	CXD2880_DVBT2_BW_RSVD4 = 0x09,
+	CXD2880_DVBT2_BW_RSVD5 = 0x0a,
+	CXD2880_DVBT2_BW_RSVD6 = 0x0b,
+	CXD2880_DVBT2_BW_RSVD7 = 0x0c,
+	CXD2880_DVBT2_BW_RSVD8 = 0x0d,
+	CXD2880_DVBT2_BW_RSVD9 = 0x0e,
+	CXD2880_DVBT2_BW_RSVD10 = 0x0f,
+	CXD2880_DVBT2_BW_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1pre_type {
+	CXD2880_DVBT2_L1PRE_TYPE_TS = 0x00,
+	CXD2880_DVBT2_L1PRE_TYPE_GS = 0x01,
+	CXD2880_DVBT2_L1PRE_TYPE_TS_GS = 0x02,
+	CXD2880_DVBT2_L1PRE_TYPE_RESERVED = 0x03,
+	CXD2880_DVBT2_L1PRE_TYPE_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_papr {
+	CXD2880_DVBT2_PAPR_0 = 0x00,
+	CXD2880_DVBT2_PAPR_1 = 0x01,
+	CXD2880_DVBT2_PAPR_2 = 0x02,
+	CXD2880_DVBT2_PAPR_3 = 0x03,
+	CXD2880_DVBT2_PAPR_RSVD1 = 0x04,
+	CXD2880_DVBT2_PAPR_RSVD2 = 0x05,
+	CXD2880_DVBT2_PAPR_RSVD3 = 0x06,
+	CXD2880_DVBT2_PAPR_RSVD4 = 0x07,
+	CXD2880_DVBT2_PAPR_RSVD5 = 0x08,
+	CXD2880_DVBT2_PAPR_RSVD6 = 0x09,
+	CXD2880_DVBT2_PAPR_RSVD7 = 0x0a,
+	CXD2880_DVBT2_PAPR_RSVD8 = 0x0b,
+	CXD2880_DVBT2_PAPR_RSVD9 = 0x0c,
+	CXD2880_DVBT2_PAPR_RSVD10 = 0x0d,
+	CXD2880_DVBT2_PAPR_RSVD11 = 0x0e,
+	CXD2880_DVBT2_PAPR_RSVD12 = 0x0f,
+	CXD2880_DVBT2_PAPR_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1post_constell {
+	CXD2880_DVBT2_L1POST_BPSK = 0x00,
+	CXD2880_DVBT2_L1POST_QPSK = 0x01,
+	CXD2880_DVBT2_L1POST_QAM16 = 0x02,
+	CXD2880_DVBT2_L1POST_QAM64 = 0x03,
+	CXD2880_DVBT2_L1POST_C_RSVD1 = 0x04,
+	CXD2880_DVBT2_L1POST_C_RSVD2 = 0x05,
+	CXD2880_DVBT2_L1POST_C_RSVD3 = 0x06,
+	CXD2880_DVBT2_L1POST_C_RSVD4 = 0x07,
+	CXD2880_DVBT2_L1POST_C_RSVD5 = 0x08,
+	CXD2880_DVBT2_L1POST_C_RSVD6 = 0x09,
+	CXD2880_DVBT2_L1POST_C_RSVD7 = 0x0a,
+	CXD2880_DVBT2_L1POST_C_RSVD8 = 0x0b,
+	CXD2880_DVBT2_L1POST_C_RSVD9 = 0x0c,
+	CXD2880_DVBT2_L1POST_C_RSVD10 = 0x0d,
+	CXD2880_DVBT2_L1POST_C_RSVD11 = 0x0e,
+	CXD2880_DVBT2_L1POST_C_RSVD12 = 0x0f,
+	CXD2880_DVBT2_L1POST_CONSTELL_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1post_cr {
+	CXD2880_DVBT2_L1POST_R1_2 = 0x00,
+	CXD2880_DVBT2_L1POST_R_RSVD1 = 0x01,
+	CXD2880_DVBT2_L1POST_R_RSVD2 = 0x02,
+	CXD2880_DVBT2_L1POST_R_RSVD3 = 0x03,
+	CXD2880_DVBT2_L1POST_R_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1post_fec_type {
+	CXD2880_DVBT2_L1POST_FEC_LDPC16K = 0x00,
+	CXD2880_DVBT2_L1POST_FEC_RSVD1 = 0x01,
+	CXD2880_DVBT2_L1POST_FEC_RSVD2 = 0x02,
+	CXD2880_DVBT2_L1POST_FEC_RSVD3 = 0x03,
+	CXD2880_DVBT2_L1POST_FEC_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_pp {
+	CXD2880_DVBT2_PP1 = 0x00,
+	CXD2880_DVBT2_PP2 = 0x01,
+	CXD2880_DVBT2_PP3 = 0x02,
+	CXD2880_DVBT2_PP4 = 0x03,
+	CXD2880_DVBT2_PP5 = 0x04,
+	CXD2880_DVBT2_PP6 = 0x05,
+	CXD2880_DVBT2_PP7 = 0x06,
+	CXD2880_DVBT2_PP8 = 0x07,
+	CXD2880_DVBT2_PP_RSVD1 = 0x08,
+	CXD2880_DVBT2_PP_RSVD2 = 0x09,
+	CXD2880_DVBT2_PP_RSVD3 = 0x0a,
+	CXD2880_DVBT2_PP_RSVD4 = 0x0b,
+	CXD2880_DVBT2_PP_RSVD5 = 0x0c,
+	CXD2880_DVBT2_PP_RSVD6 = 0x0d,
+	CXD2880_DVBT2_PP_RSVD7 = 0x0e,
+	CXD2880_DVBT2_PP_RSVD8 = 0x0f,
+	CXD2880_DVBT2_PP_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_code_rate {
+	CXD2880_DVBT2_R1_2 = 0x00,
+	CXD2880_DVBT2_R3_5 = 0x01,
+	CXD2880_DVBT2_R2_3 = 0x02,
+	CXD2880_DVBT2_R3_4 = 0x03,
+	CXD2880_DVBT2_R4_5 = 0x04,
+	CXD2880_DVBT2_R5_6 = 0x05,
+	CXD2880_DVBT2_R1_3 = 0x06,
+	CXD2880_DVBT2_R2_5 = 0x07,
+	CXD2880_DVBT2_PLP_CR_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_constell {
+	CXD2880_DVBT2_QPSK = 0x00,
+	CXD2880_DVBT2_QAM16 = 0x01,
+	CXD2880_DVBT2_QAM64 = 0x02,
+	CXD2880_DVBT2_QAM256 = 0x03,
+	CXD2880_DVBT2_CON_RSVD1 = 0x04,
+	CXD2880_DVBT2_CON_RSVD2 = 0x05,
+	CXD2880_DVBT2_CON_RSVD3 = 0x06,
+	CXD2880_DVBT2_CON_RSVD4 = 0x07,
+	CXD2880_DVBT2_CONSTELL_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_type {
+	CXD2880_DVBT2_PLP_TYPE_COMMON = 0x00,
+	CXD2880_DVBT2_PLP_TYPE_DATA1 = 0x01,
+	CXD2880_DVBT2_PLP_TYPE_DATA2 = 0x02,
+	CXD2880_DVBT2_PLP_TYPE_RSVD1 = 0x03,
+	CXD2880_DVBT2_PLP_TYPE_RSVD2 = 0x04,
+	CXD2880_DVBT2_PLP_TYPE_RSVD3 = 0x05,
+	CXD2880_DVBT2_PLP_TYPE_RSVD4 = 0x06,
+	CXD2880_DVBT2_PLP_TYPE_RSVD5 = 0x07,
+	CXD2880_DVBT2_PLP_TYPE_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_payload {
+	CXD2880_DVBT2_PLP_PAYLOAD_GFPS = 0x00,
+	CXD2880_DVBT2_PLP_PAYLOAD_GCS = 0x01,
+	CXD2880_DVBT2_PLP_PAYLOAD_GSE = 0x02,
+	CXD2880_DVBT2_PLP_PAYLOAD_TS = 0x03,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD1 = 0x04,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD2 = 0x05,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD3 = 0x06,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD4 = 0x07,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD5 = 0x08,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD6 = 0x09,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD7 = 0x0a,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD8 = 0x0b,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD9 = 0x0c,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD10 = 0x0d,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD11 = 0x0e,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD12 = 0x0f,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD13 = 0x10,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD14 = 0x11,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD15 = 0x12,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD16 = 0x13,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD17 = 0x14,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD18 = 0x15,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD19 = 0x16,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD20 = 0x17,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD21 = 0x18,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD22 = 0x19,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD23 = 0x1a,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD24 = 0x1b,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD25 = 0x1c,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD26 = 0x1d,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD27 = 0x1e,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD28 = 0x1f,
+	CXD2880_DVBT2_PLP_PAYLOAD_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_fec {
+	CXD2880_DVBT2_FEC_LDPC_16K = 0x00,
+	CXD2880_DVBT2_FEC_LDPC_64K = 0x01,
+	CXD2880_DVBT2_FEC_RSVD1 = 0x02,
+	CXD2880_DVBT2_FEC_RSVD2 = 0x03,
+	CXD2880_DVBT2_FEC_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_mode {
+	CXD2880_DVBT2_PLP_MODE_NOTSPECIFIED = 0x00,
+	CXD2880_DVBT2_PLP_MODE_NM = 0x01,
+	CXD2880_DVBT2_PLP_MODE_HEM = 0x02,
+	CXD2880_DVBT2_PLP_MODE_RESERVED = 0x03,
+	CXD2880_DVBT2_PLP_MODE_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_btype {
+	CXD2880_DVBT2_PLP_COMMON,
+	CXD2880_DVBT2_PLP_DATA
+};
+
+enum cxd2880_dvbt2_stream {
+	CXD2880_DVBT2_STREAM_GENERIC_PACKETIZED = 0x00,
+	CXD2880_DVBT2_STREAM_GENERIC_CONTINUOUS = 0x01,
+	CXD2880_DVBT2_STREAM_GENERIC_ENCAPSULATED = 0x02,
+	CXD2880_DVBT2_STREAM_TRANSPORT = 0x03,
+	CXD2880_DVBT2_STREAM_UNKNOWN = 0xff
+};
+
+struct cxd2880_dvbt2_l1pre {
+	enum cxd2880_dvbt2_l1pre_type type;
+	u8 bw_ext;
+	enum cxd2880_dvbt2_s1 s1;
+	u8 s2;
+	u8 mixed;
+	enum cxd2880_dvbt2_mode fft_mode;
+	u8 l1_rep;
+	enum cxd2880_dvbt2_guard gi;
+	enum cxd2880_dvbt2_papr papr;
+	enum cxd2880_dvbt2_l1post_constell mod;
+	enum cxd2880_dvbt2_l1post_cr cr;
+	enum cxd2880_dvbt2_l1post_fec_type fec;
+	u32 l1_post_size;
+	u32 l1_post_info_size;
+	enum cxd2880_dvbt2_pp pp;
+	u8 tx_id_availability;
+	u16 cell_id;
+	u16 network_id;
+	u16 sys_id;
+	u8 num_frames;
+	u16 num_symbols;
+	u8 regen;
+	u8 post_ext;
+	u8 num_rf_freqs;
+	u8 rf_idx;
+	enum cxd2880_dvbt2_version t2_version;
+	u8 l1_post_scrambled;
+	u8 t2_base_lite;
+	u32 crc32;
+};
+
+struct cxd2880_dvbt2_plp {
+	u8 id;
+	enum cxd2880_dvbt2_plp_type type;
+	enum cxd2880_dvbt2_plp_payload payload;
+	u8 ff;
+	u8 first_rf_idx;
+	u8 first_frm_idx;
+	u8 group_id;
+	enum cxd2880_dvbt2_plp_constell constell;
+	enum cxd2880_dvbt2_plp_code_rate plp_cr;
+	u8 rot;
+	enum cxd2880_dvbt2_plp_fec fec;
+	u16 num_blocks_max;
+	u8 frm_int;
+	u8 til_len;
+	u8 til_type;
+	u8 in_band_a_flag;
+	u8 in_band_b_flag;
+	u16 rsvd;
+	enum cxd2880_dvbt2_plp_mode plp_mode;
+	u8 static_flag;
+	u8 static_padding_flag;
+};
+
+struct cxd2880_dvbt2_l1post {
+	u16 sub_slices_per_frame;
+	u8 num_plps;
+	u8 num_aux;
+	u8 aux_cfg_rfu;
+	u8 rf_idx;
+	u32 freq;
+	u8 fef_type;
+	u32 fef_length;
+	u8 fef_intvl;
+};
+
+struct cxd2880_dvbt2_ofdm {
+	u8 mixed;
+	u8 is_miso;
+	enum cxd2880_dvbt2_mode mode;
+	enum cxd2880_dvbt2_guard gi;
+	enum cxd2880_dvbt2_pp pp;
+	u8 bw_ext;
+	enum cxd2880_dvbt2_papr papr;
+	u16 num_symbols;
+};
+
+struct cxd2880_dvbt2_bbheader {
+	enum cxd2880_dvbt2_stream stream_input;
+	u8 is_single_input_stream;
+	u8 is_constant_coding_modulation;
+	u8 issy_indicator;
+	u8 null_packet_deletion;
+	u8 ext;
+	u8 input_stream_identifier;
+	u16 user_packet_length;
+	u16 data_field_length;
+	u8 sync_byte;
+	u32 issy;
+	enum cxd2880_dvbt2_plp_mode plp_mode;
+};
+
+#endif
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..81903102b12f
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
@@ -0,0 +1,1217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt2.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T2
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "dvb_frontend.h"
+
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = {
+	{0x00, 0x00}, {0x31, 0x02},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = {
+	{0x00, 0x04}, {0x5d, 0x0b},
+};
+
+static int x_tune_dvbt2_demod_setting(struct cxd2880_tnrdmd
+				      *tnr_dmd,
+				      enum cxd2880_dtv_bandwidth
+				      bandwidth,
+				      enum cxd2880_tnrdmd_clockmode
+				      clk_mode)
+{
+	static const u8 tsif_settings[2] = { 0x01, 0x01 };
+	static const u8 init_settings[14] = {
+		0x07, 0x06, 0x01, 0xf0,	0x00, 0x00, 0x04, 0xb0, 0x00, 0x00,
+		0x09, 0x9c, 0x0e, 0x4c
+	};
+	static const u8 clk_mode_settings_a1[9] = {
+		0x52, 0x49, 0x2c, 0x51,	0x51, 0x3d, 0x15, 0x29, 0x0c
+	};
+
+	static const u8 clk_mode_settings_b1[9] = {
+		0x5d, 0x55, 0x32, 0x5c,	0x5c, 0x45, 0x17, 0x2e, 0x0d
+	};
+
+	static const u8 clk_mode_settings_c1[9] = {
+		0x60, 0x00, 0x34, 0x5e,	0x5e, 0x47, 0x18, 0x2f, 0x0e
+	};
+
+	static const u8 clk_mode_settings_a2[13] = {
+		0x04, 0xe7, 0x94, 0x92,	0x09, 0xcf, 0x7e, 0xd0, 0x49,
+		0xcd, 0xcd, 0x1f, 0x5b
+	};
+
+	static const u8 clk_mode_settings_b2[13] = {
+		0x05, 0x90, 0x27, 0x55,	0x0b, 0x20, 0x8f, 0xd6, 0xea,
+		0xc8, 0xc8, 0x23, 0x91
+	};
+
+	static const u8 clk_mode_settings_c2[13] = {
+		0x05, 0xb8, 0xd8, 0x00,	0x0b, 0x72, 0x93, 0xf3, 0x00,
+		0xcd, 0xcd, 0x24, 0x95
+	};
+
+	static const u8 clk_mode_settings_a3[5] = {
+		0x0b, 0x6a, 0xc9, 0x03, 0x33
+	};
+	static const u8 clk_mode_settings_b3[5] = {
+		0x01, 0x02, 0xe4, 0x03, 0x39
+	};
+	static const u8 clk_mode_settings_c3[5] = {
+		0x01, 0x02, 0xeb, 0x03, 0x3b
+	};
+
+	static const u8 gtdofst[2] = { 0x3f, 0xff };
+
+	static const u8 bw8_gtdofst_a[2] = { 0x19, 0xd2 };
+	static const u8 bw8_nomi_ac[6] = { 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	static const u8 bw8_nomi_b[6] = { 0x14, 0x6a, 0xaa, 0xaa, 0xab, 0x00 };
+	static const u8 bw8_sst_a[2] = { 0x06, 0x2a };
+	static const u8 bw8_sst_b[2] = { 0x06, 0x29 };
+	static const u8 bw8_sst_c[2] = { 0x06, 0x28 };
+	static const u8 bw8_mrc_a[9] = {
+		0x28, 0x00, 0x50, 0x00, 0x60, 0x00, 0x00, 0x90, 0x00
+	};
+	static const u8 bw8_mrc_b[9] = {
+		0x2d, 0x5e, 0x5a, 0xbd, 0x6c, 0xe3, 0x00, 0xa3, 0x55
+	};
+	static const u8 bw8_mrc_c[9] = {
+		0x2e, 0xaa, 0x5d, 0x55, 0x70, 0x00, 0x00, 0xa8, 0x00
+	};
+
+	static const u8 bw7_nomi_ac[6] = { 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	static const u8 bw7_nomi_b[6] = { 0x17, 0x55, 0x55, 0x55, 0x55, 0x00 };
+	static const u8 bw7_sst_a[2] = { 0x06, 0x23 };
+	static const u8 bw7_sst_b[2] = { 0x06, 0x22 };
+	static const u8 bw7_sst_c[2] = { 0x06, 0x21 };
+	static const u8 bw7_mrc_a[9] = {
+		0x2d, 0xb6, 0x5b, 0x6d,	0x6d, 0xb6, 0x00, 0xa4, 0x92
+	};
+	static const u8 bw7_mrc_b[9] = {
+		0x33, 0xda, 0x67, 0xb4,	0x7c, 0x71, 0x00, 0xba, 0xaa
+	};
+	static const u8 bw7_mrc_c[9] = {
+		0x35, 0x55, 0x6a, 0xaa,	0x80, 0x00, 0x00, 0xc0, 0x00
+	};
+
+	static const u8 bw6_nomi_ac[6] = { 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	static const u8 bw6_nomi_b[6] = { 0x1b, 0x38, 0xe3, 0x8e, 0x39, 0x00 };
+	static const u8 bw6_sst_a[2] = { 0x06, 0x1c };
+	static const u8 bw6_sst_b[2] = { 0x06, 0x1b };
+	static const u8 bw6_sst_c[2] = { 0x06, 0x1a };
+	static const u8 bw6_mrc_a[9] = {
+		0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00
+	};
+	static const u8 bw6_mrc_b[9] = {
+		0x3c, 0x7e, 0x78, 0xfc,	0x91, 0x2f, 0x00, 0xd9, 0xc7
+	};
+	static const u8 bw6_mrc_c[9] = {
+		0x3e, 0x38, 0x7c, 0x71,	0x95, 0x55, 0x00, 0xdf, 0xff
+	};
+
+	static const u8 bw5_nomi_ac[6] = { 0x21, 0x99, 0x99, 0x99, 0x9a, 0x00 };
+	static const u8 bw5_nomi_b[6] = { 0x20, 0xaa, 0xaa, 0xaa, 0xab, 0x00 };
+	static const u8 bw5_sst_a[2] = { 0x06, 0x15 };
+	static const u8 bw5_sst_b[2] = { 0x06, 0x15 };
+	static const u8 bw5_sst_c[2] = { 0x06, 0x14 };
+	static const u8 bw5_mrc_a[9] = {
+		0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xe6, 0x66
+	};
+	static const u8 bw5_mrc_b[9] = {
+		0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x01, 0x05, 0x55
+	};
+	static const u8 bw5_mrc_c[9] = {
+		0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x01, 0x0c, 0xcc
+	};
+
+	static const u8 bw1_7_nomi_a[6] = {
+		0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03
+	};
+	static const u8 bw1_7_nomi_c[6] = {
+		0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03
+	};
+	static const u8 bw1_7_nomi_b[6] = {
+		0x65, 0x2b, 0xa4, 0xcd, 0xd8, 0x03
+	};
+	static const u8 bw1_7_sst_a[2] = { 0x06, 0x0c };
+	static const u8 bw1_7_sst_b[2] = { 0x06, 0x0c };
+	static const u8 bw1_7_sst_c[2] = { 0x06, 0x0b };
+	static const u8 bw1_7_mrc_a[9] = {
+		0x40, 0x00, 0x6a, 0xaa,	0x80, 0x00, 0x02, 0xc9, 0x8f
+	};
+	static const u8 bw1_7_mrc_b[9] = {
+		0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x03, 0x29, 0x5d
+	};
+	static const u8 bw1_7_mrc_c[9] = {
+		0x4a, 0xaa, 0x7c, 0x71,	0x95, 0x55, 0x03, 0x40, 0x7d
+	};
+
+	const u8 *data = NULL;
+	const u8 *data2 = NULL;
+	const u8 *data3 = NULL;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_SYS,
+					  tune_dmd_setting_seq1,
+					  ARRAY_SIZE(tune_dmd_setting_seq1));
+	if (ret)
+		return ret;
+
+	ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+					  CXD2880_IO_TGT_DMD,
+					  tune_dmd_setting_seq2,
+					  ARRAY_SIZE(tune_dmd_setting_seq2));
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x00);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0xce, tsif_settings, 2);
+		if (ret)
+			return ret;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x20);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x8a, init_settings[0]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x90, init_settings[1]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x25);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xf0, &init_settings[2], 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x2a);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xdc, init_settings[4]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0xde, init_settings[5]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x2d);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x73, &init_settings[6], 4);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x8f, &init_settings[10], 4);
+	if (ret)
+		return ret;
+
+	switch (clk_mode) {
+	case CXD2880_TNRDMD_CLOCKMODE_A:
+		data = clk_mode_settings_a1;
+		data2 = clk_mode_settings_a2;
+		data3 = clk_mode_settings_a3;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_B:
+		data = clk_mode_settings_b1;
+		data2 = clk_mode_settings_b2;
+		data3 = clk_mode_settings_b3;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_C:
+		data = clk_mode_settings_c1;
+		data2 = clk_mode_settings_c2;
+		data3 = clk_mode_settings_c3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x1d, &data[0], 3);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x22, data[3]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x24, data[4]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x26, data[5]);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x29, &data[6], 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x2d, data[8]);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x2e, &data2[0], 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x35, &data2[6], 7);
+		if (ret)
+			return ret;
+	}
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x3c, &data3[0], 2);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x56, &data3[2], 3);
+	if (ret)
+		return ret;
+
+	switch (bandwidth) {
+	case CXD2880_DTV_BW_8_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw8_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw8_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x00);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw8_gtdofst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = gtdofst;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, data, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw8_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw8_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw8_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw8_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw8_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw8_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	case CXD2880_DTV_BW_7_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw7_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw7_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x02);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, gtdofst, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw7_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw7_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw7_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw7_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw7_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw7_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	case CXD2880_DTV_BW_6_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw6_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw6_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x04);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, gtdofst, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw6_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw6_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw6_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw6_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw6_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw6_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	case CXD2880_DTV_BW_5_MHZ:
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw5_nomi_ac;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw5_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x06);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, gtdofst, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw5_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw5_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw5_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw5_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw5_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw5_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	case CXD2880_DTV_BW_1_7_MHZ:
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw1_7_nomi_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw1_7_nomi_c;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw1_7_nomi_b;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x10, data, 6);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x4a, 0x03);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x19, gtdofst, 2);
+		if (ret)
+			return ret;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = bw1_7_sst_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = bw1_7_sst_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = bw1_7_sst_c;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x1b, data, 2);
+		if (ret)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = bw1_7_mrc_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = bw1_7_mrc_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = bw1_7_mrc_c;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+						      CXD2880_IO_TGT_DMD,
+						      0x4b, data, 9);
+			if (ret)
+				return ret;
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x00);
+	if (ret)
+		return ret;
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xfd, 0x01);
+}
+
+static int x_sleep_dvbt2_demod_setting(struct cxd2880_tnrdmd
+				       *tnr_dmd)
+{
+	static const u8 difint_clip[] = {
+		0, 1, 0, 2, 0, 4, 0, 8, 0, 16, 0, 32
+	};
+	int ret = 0;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0x00, 0x1d);
+		if (ret)
+			return ret;
+
+		ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+					      CXD2880_IO_TGT_DMD,
+					      0x47, difint_clip, 12);
+	}
+
+	return ret;
+}
+
+static int dvbt2_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+			     enum cxd2880_dvbt2_profile profile)
+{
+	u8 t2_mode_tune_mode = 0;
+	u8 seq_not2_dtime = 0;
+	u8 dtime1 = 0;
+	u8 dtime2 = 0;
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	switch (tnr_dmd->clk_mode) {
+	case CXD2880_TNRDMD_CLOCKMODE_A:
+		dtime1 = 0x27;
+		dtime2 = 0x0c;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_B:
+		dtime1 = 0x2c;
+		dtime2 = 0x0d;
+		break;
+	case CXD2880_TNRDMD_CLOCKMODE_C:
+		dtime1 = 0x2e;
+		dtime2 = 0x0e;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (profile) {
+	case CXD2880_DVBT2_PROFILE_BASE:
+		t2_mode_tune_mode = 0x01;
+		seq_not2_dtime = dtime2;
+		break;
+
+	case CXD2880_DVBT2_PROFILE_LITE:
+		t2_mode_tune_mode = 0x05;
+		seq_not2_dtime = dtime1;
+		break;
+
+	case CXD2880_DVBT2_PROFILE_ANY:
+		t2_mode_tune_mode = 0x00;
+		seq_not2_dtime = dtime1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x2e);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x10, t2_mode_tune_mode);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x04);
+	if (ret)
+		return ret;
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0x2c, seq_not2_dtime);
+}
+
+int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_dvbt2_tune_param
+			       *tune_param)
+{
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN &&
+	    tune_param->profile == CXD2880_DVBT2_PROFILE_ANY)
+		return -ENOTTY;
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT2,
+						tune_param->center_freq_khz,
+						tune_param->bandwidth, 0, 0);
+	if (ret)
+		return ret;
+
+	ret =
+	    x_tune_dvbt2_demod_setting(tnr_dmd, tune_param->bandwidth,
+				       tnr_dmd->clk_mode);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    x_tune_dvbt2_demod_setting(tnr_dmd->diver_sub,
+					       tune_param->bandwidth,
+					       tnr_dmd->diver_sub->clk_mode);
+		if (ret)
+			return ret;
+	}
+
+	ret = dvbt2_set_profile(tnr_dmd, tune_param->profile);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    dvbt2_set_profile(tnr_dmd->diver_sub, tune_param->profile);
+		if (ret)
+			return ret;
+	}
+
+	if (tune_param->data_plp_id == CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO)
+		ret = cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 1, 0);
+	else
+		ret =
+		    cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 0,
+					     (u8)(tune_param->data_plp_id));
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_dvbt2_tune_param
+			       *tune_param)
+{
+	u8 en_fef_intmtnt_ctrl = 1;
+	int ret;
+
+	if (!tnr_dmd || !tune_param)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	switch (tune_param->profile) {
+	case CXD2880_DVBT2_PROFILE_BASE:
+		en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_base;
+		break;
+	case CXD2880_DVBT2_PROFILE_LITE:
+		en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_lite;
+		break;
+	case CXD2880_DVBT2_PROFILE_ANY:
+		if (tnr_dmd->en_fef_intmtnt_base &&
+		    tnr_dmd->en_fef_intmtnt_lite)
+			en_fef_intmtnt_ctrl = 1;
+		else
+			en_fef_intmtnt_ctrl = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting2(tnr_dmd,
+						CXD2880_DTV_SYS_DVBT2,
+						en_fef_intmtnt_ctrl);
+	if (ret)
+		return ret;
+
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+	tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+	tnr_dmd->sys = CXD2880_DTV_SYS_DVBT2;
+	tnr_dmd->bandwidth = tune_param->bandwidth;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+		tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT2;
+		tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+	}
+
+	return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+				       *tnr_dmd)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = x_sleep_dvbt2_demod_setting(tnr_dmd);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+		ret = x_sleep_dvbt2_demod_setting(tnr_dmd->diver_sub);
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+					  *tnr_dmd,
+					  enum
+					  cxd2880_tnrdmd_lock_result
+					  *lock)
+{
+	int ret;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if (!tnr_dmd || !lock)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					       &unlock_detected);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (sync_stat == 6)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (sync_stat == 6) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						   &unlock_detected_sub);
+	if (ret)
+		return ret;
+
+	if (sync_stat == 6)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+	else if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+				       *tnr_dmd,
+				       enum
+				       cxd2880_tnrdmd_lock_result
+				       *lock)
+{
+	int ret;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if (!tnr_dmd || !lock)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					       &unlock_detected);
+	if (ret)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (ts_lock)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (ts_lock) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	} else if (!unlock_detected) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						   &unlock_detected_sub);
+	if (ret)
+		return ret;
+
+	if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+				     *tnr_dmd, u8 auto_plp,
+				     u8 plp_id)
+{
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x23);
+	if (ret)
+		return ret;
+
+	if (!auto_plp) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_DMD,
+					     0xaf, plp_id);
+		if (ret)
+			return ret;
+	}
+
+	return tnr_dmd->io->write_reg(tnr_dmd->io,
+				      CXD2880_IO_TGT_DMD,
+				      0xad, auto_plp ? 0x00 : 0x01);
+}
+
+int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+					   *tnr_dmd)
+{
+	struct cxd2880_dvbt2_ofdm ofdm;
+	static const u8 data[] = { 0, 8, 0, 16, 0, 32, 0, 64, 0, 128, 1, 0};
+	int ret;
+
+	if (!tnr_dmd)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE)
+		return 0;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd, &ofdm);
+	if (ret)
+		return ret;
+
+	if (!ofdm.mixed)
+		return 0;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x1d);
+	if (ret)
+		return ret;
+
+	return tnr_dmd->io->write_regs(tnr_dmd->io,
+				       CXD2880_IO_TGT_DMD,
+				       0x47, data, 12);
+}
+
+int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+					    *tnr_dmd,
+					    u8 *l1_post_valid)
+{
+	int ret;
+
+	u8 data;
+
+	if (!tnr_dmd || !l1_post_valid)
+		return -EINVAL;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return -EINVAL;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+	    tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return -EINVAL;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x00, 0x0b);
+	if (ret)
+		return ret;
+
+	ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD,
+				     0x86, &data, 1);
+	if (ret)
+		return ret;
+
+	*l1_post_valid = data & 0x01;
+
+	return ret;
+}
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..7108e3540093
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T2
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT2_H
+#define CXD2880_TNRDMD_DVBT2_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+enum cxd2880_tnrdmd_dvbt2_tune_info {
+	CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK,
+	CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID
+};
+
+struct cxd2880_dvbt2_tune_param {
+	u32 center_freq_khz;
+	enum cxd2880_dtv_bandwidth bandwidth;
+	u16 data_plp_id;
+	enum cxd2880_dvbt2_profile profile;
+	enum cxd2880_tnrdmd_dvbt2_tune_info tune_info;
+};
+
+#define CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO  0xffff
+
+int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_dvbt2_tune_param
+			       *tune_param);
+
+int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+			       struct cxd2880_dvbt2_tune_param
+			       *tune_param);
+
+int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+				       *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+					  *tnr_dmd,
+					  enum
+					  cxd2880_tnrdmd_lock_result
+					  *lock);
+
+int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+				       *tnr_dmd,
+				       enum
+				       cxd2880_tnrdmd_lock_result
+				       *lock);
+
+int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+				     *tnr_dmd, u8 auto_plp,
+				     u8 plp_id);
+
+int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+					   *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+					    *tnr_dmd,
+					    u8 *l1_post_valid);
+
+#endif
-- 
2.15.1

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

* [PATCH v5 11/12] [media] cxd2880: Add DVB-T2 monitor functions
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (9 preceding siblings ...)
  2018-01-18  8:55 ` [PATCH v5 10/12] [media] cxd2880: Add DVB-T2 control functions for the driver Yasunari.Takiguchi
@ 2018-01-18  8:57 ` Yasunari.Takiguchi
  2018-01-18  8:59 ` [PATCH v5 12/12] [media] cxd2880: Add all Makefile, Kconfig files and Update MAINTAINERS file for the driver Yasunari.Takiguchi
  2018-02-26  0:49 ` [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:57 UTC (permalink / raw)
  To: 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>

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 V5
   Using SPDX-License-Identifier
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
      -removed unnecessary if()        
      -modified return error code
      -removed unnecessary parentheses 
      -modified for "Lines should not end with a '(' "
      -removed unnecessary functions
   drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
      -removed unnecessary functions

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

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

* [PATCH v5 12/12] [media] cxd2880: Add all Makefile, Kconfig files and Update MAINTAINERS file for the driver
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (10 preceding siblings ...)
  2018-01-18  8:57 ` [PATCH v5 11/12] [media] cxd2880: Add DVB-T2 monitor functions Yasunari.Takiguchi
@ 2018-01-18  8:59 ` Yasunari.Takiguchi
  2018-02-26  0:49 ` [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-01-18  8:59 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>

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 V5
   drivers/media/dvb-frontends/cxd2880/Makefile
       -Using SPDX-License-Identifier
       -removed cxd2880_stopwatch_port.o
   drivers/media/dvb-frontends/cxd2880/Kconfig
       -Using SPDX-License-Identifier

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  |  8 ++++++++
 drivers/media/dvb-frontends/cxd2880/Makefile | 19 +++++++++++++++++++
 drivers/media/spi/Kconfig                    | 14 ++++++++++++++
 drivers/media/spi/Makefile                   |  5 +++++
 7 files changed, 58 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 18994806e441..fcdbd7874ffb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8549,6 +8549,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 d17722eb4456..7fb626c00a39 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 d025eb373842..7c7aca05678a 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -130,3 +130,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..9d989676e800
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DVB_CXD2880
+	tristate "Sony CXD2880 DVB-T2/T tuner + demodulator"
+	depends on DVB_CORE && SPI
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y when you want to support this frontend.
\ 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..65a5d37f28cc
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+
+cxd2880-objs := cxd2880_common.o \
+		cxd2880_devio_spi.o \
+		cxd2880_integ.o \
+		cxd2880_io.o \
+		cxd2880_spi_device.o \
+		cxd2880_tnrdmd.o \
+		cxd2880_tnrdmd_dvbt2.o \
+		cxd2880_tnrdmd_dvbt2_mon.o \
+		cxd2880_tnrdmd_dvbt.o \
+		cxd2880_tnrdmd_dvbt_mon.o\
+		cxd2880_tnrdmd_mon.o\
+		cxd2880_top.o
+
+obj-$(CONFIG_DVB_CXD2880) += cxd2880.o
+
+ccflags-y += -Idrivers/media/dvb-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..9e536777a330 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
-- 
2.15.1

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

* RE: [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (11 preceding siblings ...)
  2018-01-18  8:59 ` [PATCH v5 12/12] [media] cxd2880: Add all Makefile, Kconfig files and Update MAINTAINERS file for the driver Yasunari.Takiguchi
@ 2018-02-26  0:49 ` Yasunari.Takiguchi
  12 siblings, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-02-26  0:49 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Masayuki.Yamamoto, Hideki.Nozawa,
	Kota.Yonezawa, Toshihiko.Matsumoto, Satoshi.C.Watanabe,
	Yasunari.Takiguchi

Hi, all

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

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

Thanks,
Takiguchi

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

* Re: [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2018-01-18  8:46 ` [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Yasunari.Takiguchi
@ 2018-03-07 10:15   ` Mauro Carvalho Chehab
  2018-03-08  0:51     ` Yasunari.Takiguchi
  2018-04-06  0:28     ` Yasunari.Takiguchi
  0 siblings, 2 replies; 17+ messages in thread
From: Mauro Carvalho Chehab @ 2018-03-07 10:15 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki.Yamamoto, Hideki.Nozawa, Kota.Yonezawa,
	Toshihiko.Matsumoto, Satoshi.C.Watanabe

Em Thu, 18 Jan 2018 17:46:10 +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.

Thanks for the patches!

The patch series look ok. Just a few nitpicks that could be solved
later.

I had to apply a few patches to make it build and remove some
warnings with W=1. Patches sent.

With that, I'm applying this series.

Regards,
Mauro

> 
> 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 V5
>    Using SPDX-License-Identifier
>    drivers/media/spi/cxd2880-spi.c
>       -modified typo about "ivnalid" -> "invalid"
>       -modified typo about "drvier" -> "driver"
>       -removed unnecessary if() 
>       -modified return error code
>       -reduction of valiable names
>       -removed unnecessary parentheses 
>       -changed members of struct cxd2880_ts_buf_info
> 
> 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 | 670 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 670 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..857e4c0d7a92
> --- /dev/null
> +++ b/drivers/media/spi/cxd2880-spi.c
> @@ -0,0 +1,670 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * cxd2880-spi.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * SPI adapter
> + *
> + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
> +
> +#include <linux/spi/spi.h>
> +#include <linux/ktime.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_PKT 300
> +
> +struct cxd2880_ts_buf_info {
> +	u8 read_ready:1;
> +	u8 almost_full:1;
> +	u8 almost_empty:1;
> +	u8 overflow:1;
> +	u8 underflow:1;
> +	u16 pkt_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;
> +
> +	if (!spi || !data) {
> +		pr_err("invalid arg\n");
> +		return -EINVAL;
> +	}
> +
> +	memset(&tx, 0, sizeof(tx));

Nitpick:

	instead, you could just declare tx as:

		struct spi_transfer tx = {};

and get rid of memset (same applies to similar code blocks).


> +	tx.tx_buf = data;
> +	tx.len = size;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&tx, &msg);
> +
> +	return spi_sync(spi, &msg);
> +}
> +
> +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;
> +	}

It is better to use dev_err(spi->dev, ...) instead of pr_err().


> +
> +	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;
> +	data[2] = packet_num;
> +
> +	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->pkt_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("invalid 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);
> +	} 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);
> +	}
> +
> +	return ret;
> +}
> +
> +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;
> +	ktime_t start;
> +	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 = ktime_get();
> +	while (!kthread_should_stop()) {
> +		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.pkt_num > MAX_TRANS_PKT) {
> +			for (i = 0; i < info.pkt_num / MAX_TRANS_PKT; i++) {
> +				cxd2880_spi_read_ts(dvb_spi->spi,
> +						    dvb_spi->ts_buf,
> +						    MAX_TRANS_PKT);
> +				dvb_dmx_swfilter(&dvb_spi->demux,
> +						 dvb_spi->ts_buf,
> +						 MAX_TRANS_PKT * 188);
> +			}
> +			start = ktime_get();
> +		} else if ((info.pkt_num > 0) &&
> +			   (ktime_to_ms(ktime_sub(ktime_get(), start)) >= 500)) {
> +			cxd2880_spi_read_ts(dvb_spi->spi,
> +					    dvb_spi->ts_buf,
> +					    info.pkt_num);
> +			dvb_dmx_swfilter(&dvb_spi->demux,
> +					 dvb_spi->ts_buf,
> +					 info.pkt_num * 188);
> +			start = ktime_get();
> +		} 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 -EINVAL;
> +	}
> +
> +	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");

"Assumed bug"? What do you mean?

If the hardware filters are full, it is likely an userspace problem,
not a Kernel bug.

> +			return -EINVAL;
> +		}
> +		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_PKT * 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 + demod driver SPI adapter");
> +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
> +MODULE_LICENSE("GPL v2");



Thanks,
Mauro

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

* RE: [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2018-03-07 10:15   ` Mauro Carvalho Chehab
@ 2018-03-08  0:51     ` Yasunari.Takiguchi
  2018-04-06  0:28     ` Yasunari.Takiguchi
  1 sibling, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-03-08  0:51 UTC (permalink / raw)
  To: mchehab
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki.Yamamoto, Hideki.Nozawa, Kota.Yonezawa,
	Toshihiko.Matsumoto, Satoshi.C.Watanabe, Yasunari.Takiguchi

Dear Mauro

I am very glad to hear your message.

Being busy, thank you for taking care of fixing patches as well also.
And we will improve about your below comments continuously.

Regards & Thanks
Takiguchi

> -----Original Message-----
> From: Mauro Carvalho Chehab [mailto:mchehab@s-opensource.com]
> Sent: Wednesday, March 7, 2018 7:15 PM
> To: Takiguchi, Yasunari (SSS)
> Cc: linux-kernel@vger.kernel.org; devicetree@vger.kernel.org;
> linux-media@vger.kernel.org; tbird20d@gmail.com;
> frowand.list@gmail.com; Yamamoto, Masayuki (SSS); Nozawa, Hideki (STWN);
> Yonezawa, Kota (SSS); Matsumoto, Toshihiko (SSS); Watanabe, Satoshi (SSS)
> Subject: Re: [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880
> SPI interface
> 
> Em Thu, 18 Jan 2018 17:46:10 +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.
> 
> Thanks for the patches!
> 
> The patch series look ok. Just a few nitpicks that could be solved later.
> 
> I had to apply a few patches to make it build and remove some warnings
> with W=1. Patches sent.
> 
> With that, I'm applying this series.
> 
> Regards,
> Mauro
> 
> >
> > 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 V5
> >    Using SPDX-License-Identifier
> >    drivers/media/spi/cxd2880-spi.c
> >       -modified typo about "ivnalid" -> "invalid"
> >       -modified typo about "drvier" -> "driver"
> >       -removed unnecessary if()
> >       -modified return error code
> >       -reduction of valiable names
> >       -removed unnecessary parentheses
> >       -changed members of struct cxd2880_ts_buf_info
> >
> > 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 | 670
> > ++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 670 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..857e4c0d7a92
> > --- /dev/null
> > +++ b/drivers/media/spi/cxd2880-spi.c
> > @@ -0,0 +1,670 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * cxd2880-spi.c
> > + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> > + * SPI adapter
> > + *
> > + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions
> > +Corporation  */
> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
> > +
> > +#include <linux/spi/spi.h>
> > +#include <linux/ktime.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_PKT 300
> > +
> > +struct cxd2880_ts_buf_info {
> > +	u8 read_ready:1;
> > +	u8 almost_full:1;
> > +	u8 almost_empty:1;
> > +	u8 overflow:1;
> > +	u8 underflow:1;
> > +	u16 pkt_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;
> > +
> > +	if (!spi || !data) {
> > +		pr_err("invalid arg\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	memset(&tx, 0, sizeof(tx));
> 
> Nitpick:
> 
> 	instead, you could just declare tx as:
> 
> 		struct spi_transfer tx = {};
> 
> and get rid of memset (same applies to similar code blocks).
> 
> 
> > +	tx.tx_buf = data;
> > +	tx.len = size;
> > +
> > +	spi_message_init(&msg);
> > +	spi_message_add_tail(&tx, &msg);
> > +
> > +	return spi_sync(spi, &msg);
> > +}
> > +
> > +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;
> > +	}
> 
> It is better to use dev_err(spi->dev, ...) instead of pr_err().
> 
> 
> > +
> > +	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;
> > +	data[2] = packet_num;
> > +
> > +	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->pkt_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("invalid 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);
> > +	} 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);
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +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;
> > +	ktime_t start;
> > +	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 = ktime_get();
> > +	while (!kthread_should_stop()) {
> > +		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.pkt_num > MAX_TRANS_PKT) {
> > +			for (i = 0; i < info.pkt_num / MAX_TRANS_PKT; i++)
> {
> > +				cxd2880_spi_read_ts(dvb_spi->spi,
> > +						    dvb_spi->ts_buf,
> > +						    MAX_TRANS_PKT);
> > +				dvb_dmx_swfilter(&dvb_spi->demux,
> > +						 dvb_spi->ts_buf,
> > +						 MAX_TRANS_PKT * 188);
> > +			}
> > +			start = ktime_get();
> > +		} else if ((info.pkt_num > 0) &&
> > +			   (ktime_to_ms(ktime_sub(ktime_get(), start))
> >= 500)) {
> > +			cxd2880_spi_read_ts(dvb_spi->spi,
> > +					    dvb_spi->ts_buf,
> > +					    info.pkt_num);
> > +			dvb_dmx_swfilter(&dvb_spi->demux,
> > +					 dvb_spi->ts_buf,
> > +					 info.pkt_num * 188);
> > +			start = ktime_get();
> > +		} 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 -EINVAL;
> > +	}
> > +
> > +	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");
> 
> "Assumed bug"? What do you mean?
> 
> If the hardware filters are full, it is likely an userspace problem, not
> a Kernel bug.
> 
> > +			return -EINVAL;
> > +		}
> > +		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_PKT * 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 + demod driver SPI
> > +adapter"); MODULE_AUTHOR("Sony Semiconductor Solutions
> Corporation");
> > +MODULE_LICENSE("GPL v2");
> 
> 
> 
> Thanks,
> Mauro

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

* RE: [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface
  2018-03-07 10:15   ` Mauro Carvalho Chehab
  2018-03-08  0:51     ` Yasunari.Takiguchi
@ 2018-04-06  0:28     ` Yasunari.Takiguchi
  1 sibling, 0 replies; 17+ messages in thread
From: Yasunari.Takiguchi @ 2018-04-06  0:28 UTC (permalink / raw)
  To: mchehab
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki.Yamamoto, Hideki.Nozawa, Kota.Yonezawa,
	Toshihiko.Matsumoto, Satoshi.C.Watanabe, Yasunari.Takiguchi

Hi, Mauro

> > +	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;
> > +	}
> 
> It is better to use dev_err(spi->dev, ...) instead of pr_err().

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?

Takiguchi

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

end of thread, other threads:[~2018-04-06  0:28 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-18  8:40 [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
2018-01-18  8:43 ` [PATCH v5 01/12] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
2018-01-18  8:46 ` [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Yasunari.Takiguchi
2018-03-07 10:15   ` Mauro Carvalho Chehab
2018-03-08  0:51     ` Yasunari.Takiguchi
2018-04-06  0:28     ` Yasunari.Takiguchi
2018-01-18  8:47 ` [PATCH v5 03/12] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
2018-01-18  8:48 ` [PATCH v5 04/12] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
2018-01-18  8:50 ` [PATCH v5 05/12] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
2018-01-18  8:51 ` [PATCH v5 06/12] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
2018-01-18  8:52 ` [PATCH v5 07/12] [media] cxd2880: Add top level of " Yasunari.Takiguchi
2018-01-18  8:53 ` [PATCH v5 08/12] [media] cxd2880: Add DVB-T control functions " Yasunari.Takiguchi
2018-01-18  8:54 ` [PATCH v5 09/12] [media] cxd2880: Add DVB-T monitor functions Yasunari.Takiguchi
2018-01-18  8:55 ` [PATCH v5 10/12] [media] cxd2880: Add DVB-T2 control functions for the driver Yasunari.Takiguchi
2018-01-18  8:57 ` [PATCH v5 11/12] [media] cxd2880: Add DVB-T2 monitor functions Yasunari.Takiguchi
2018-01-18  8:59 ` [PATCH v5 12/12] [media] cxd2880: Add all Makefile, Kconfig files and Update MAINTAINERS file for the driver Yasunari.Takiguchi
2018-02-26  0:49 ` [PATCH v5 00/12] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi

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